装饰器模式 (Decorator Pattern)
模式定义
装饰器模式是一种结构型设计模式,它动态地给对象添加额外职责,比继承更灵活。装饰器提供了比继承更有弹性的替代方案。
核心要点
- 装饰器和被装饰对象实现相同接口
- 装饰器持有被装饰对象的引用
- 可以动态添加功能
实现示例:咖啡店
组件接口
java
// 饮品接口
public interface Beverage {
String getDescription();
double getCost();
}
// 调料接口(装饰器接口)
public interface CondimentDecorator extends Beverage {
// 扩展饮料,添加调料
}基础组件
java
// 浓缩咖啡
public class Espresso implements Beverage {
@Override
public String getDescription() {
return "浓缩咖啡";
}
@Override
public double getCost() {
return 15.0;
}
}
// 深烘焙咖啡
public class DarkRoast implements Beverage {
@Override
public String getDescription() {
return "深烘焙咖啡";
}
@Override
public double getCost() {
return 12.0;
}
}
// 综合咖啡
public class HouseBlend implements Beverage {
@Override
public String getDescription() {
return "综合咖啡";
}
@Override
public double getCost() {
return 10.0;
}
}装饰器(调料)
java
// 调料装饰器抽象类
public abstract class CondimentDecorator implements CondimentDecorator {
protected final Beverage beverage;
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription();
}
@Override
public double getCost() {
return beverage.getCost();
}
}
// 牛奶装饰器
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 牛奶";
}
@Override
public double getCost() {
return beverage.getCost() + 3.0;
}
}
// 摩卡装饰器
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 摩卡";
}
@Override
public double getCost() {
return beverage.getCost() + 5.0;
}
}
// 奶泡装饰器
public class Whip extends CondimentDecorator {
public Whip(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 奶泡";
}
@Override
public double getCost() {
return beverage.getCost() + 2.0;
}
}
// 焦糖装饰器
public class Caramel extends CondimentDecorator {
public Caramel(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 焦糖";
}
@Override
public double getCost() {
return beverage.getCost() + 4.0;
}
}使用示例
java
public class CoffeeShopDemo {
public static void main(String[] args) {
// 1. 浓缩咖啡
Beverage beverage1 = new Espresso();
System.out.println(beverage1.getDescription() + " ¥" + beverage1.getCost());
// 2. 深烘焙 + 摩卡 + 奶泡
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2); // 双份摩卡
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + " ¥" + beverage2.getCost());
// 3. 综合 + 牛奶 + 焦糖 + 奶泡
Beverage beverage3 = new HouseBlend();
beverage3 = new Milk(beverage3);
beverage3 = new Caramel(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription() + " ¥" + beverage3.getCost());
}
}输出:
浓缩咖啡 ¥15.0
深烘焙咖啡 + 摩卡 + 摩卡 + 奶泡 ¥24.0
综合咖啡 + 牛奶 + 焦糖 + 奶泡 ¥19.0Java IO 中的装饰器模式
Java I/O 流就是典型的装饰器模式应用。
java
public class JavaIODecoratorDemo {
public static void main(String[] args) throws IOException {
// 基础流
InputStream is = new FileInputStream("test.txt");
// 装饰:添加缓冲功能
InputStream bis = new BufferedInputStream(is);
// 装饰:添加行号功能
LineNumberReader reader = new LineNumberReader(
new BufferedReader(
new InputStreamReader(
new FileInputStream("test.txt"), "UTF-8")));
// 读取内容
String line;
while ((line = reader.readLine()) != null) {
System.out.println(reader.getLineNumber() + ": " + line);
}
// 经典的 IO 装饰器链
// new BufferedReader(
// new InputStreamReader(
// new FileInputStream("file.txt"), "UTF-8"))
}
}IO 装饰器层次结构
InputStream (抽象组件)
├── FileInputStream (具体组件)
├── ByteArrayInputStream (具体组件)
├── FilterInputStream (装饰器基类)
│ ├── BufferedInputStream (具体装饰器)
│ ├── DataInputStream (具体装饰器)
│ └── PushbackInputStream (具体装饰器)统计装饰器示例
在不修改原有类的情况下,添加性能统计功能。
java
// 数据访问接口
public interface DataService {
String queryData(String key);
void saveData(String key, String value);
}
// 具体实现
public class DatabaseDataService implements DataService {
@Override
public String queryData(String key) {
// 模拟数据库查询
return "value_" + key;
}
@Override
public void saveData(String key, String value) {
// 模拟数据库保存
System.out.println("保存数据: " + key + " = " + value);
}
}
// 性能统计装饰器
public class PerformanceDecorator implements DataService {
private final DataService delegate;
public PerformanceDecorator(DataService delegate) {
this.delegate = delegate;
}
@Override
public String queryData(String key) {
long start = System.currentTimeMillis();
String result = delegate.queryData(key);
long end = System.currentTimeMillis();
System.out.println("查询耗时: " + (end - start) + "ms, key=" + key);
return result;
}
@Override
public void saveData(String key, String value) {
long start = System.currentTimeMillis();
delegate.saveData(key, value);
long end = System.currentTimeMillis();
System.out.println("保存耗时: " + (end - start) + "ms");
}
}
// 日志装饰器
public class LoggingDecorator implements DataService {
private final DataService delegate;
public LoggingDecorator(DataService delegate) {
this.delegate = delegate;
}
@Override
public String queryData(String key) {
System.out.println("[LOG] 查询数据: " + key);
String result = delegate.queryData(key);
System.out.println("[LOG] 查询结果: " + result);
return result;
}
@Override
public void saveData(String key, String value) {
System.out.println("[LOG] 保存数据: " + key + " = " + value);
delegate.saveData(key, value);
System.out.println("[LOG] 保存完成");
}
}
// 使用
public class DecoratorDemo {
public static void main(String[] args) {
// 组合装饰器
DataService service = new PerformanceDecorator(
new LoggingDecorator(
new DatabaseDataService()));
service.queryData("user_1");
System.out.println("---");
service.saveData("user_2", "张三");
}
}装饰器 vs 继承 vs 代理
| 特性 | 装饰器 | 继承 | 代理 |
|---|---|---|---|
| 功能扩展 | 动态 | 静态 | 动态 |
| 运行时行为 | 可变 | 固定 | 可变 |
| 目的 | 增强功能 | 扩展功能 | 控制访问 |
| 组合方式 | 可多层装饰 | 单继承 | 一对一 |
优缺点
优点:
- 比继承更灵活,可以动态添加功能
- 符合开闭原则,不修改原有代码
- 可以自由组合功能
- 比继承更细粒度的控制
缺点:
- 会产生很多小类,增加复杂度
- 多层装饰时,调试困难
- 移除装饰器需要修改客户端代码
适用场景
- 需要动态、透明地给对象添加职责
- 不可以通过继承扩展功能时
- Java I/O 流的设计
- 日志、安全、事务等横切关注点