装饰器模式 (Decorator)
📖 通俗理解
想象你去咖啡店买咖啡:
- 基础款:美式咖啡 15元
- 加牛奶:+5元
- 加糖:+2元
- 加奶泡:+3元
你可以自由组合:美式 + 牛奶 + 糖 = 22元
装饰器模式就是:动态地给对象添加额外的功能,而不需要修改原有类。就像给咖啡加料一样,一层层"装饰"上去。
🎯 解决什么问题?
问题:如果用继承来扩展功能,会导致类爆炸。
咖啡
├── 美式
│ ├── 美式+牛奶
│ ├── 美式+糖
│ ├── 美式+牛奶+糖
│ └── ...
├── 拿铁
│ ├── 拿铁+牛奶
│ └── ...
└── ...
继承是静态的,在编译时就确定了。而装饰器是动态的,可以在运行时自由组合。
🌰 生活中的例子
- 咖啡加料:基础咖啡 + 牛奶 + 糖
- 手机壳:手机 + 保护壳 + 挂绑
- 穿衣服:人 + 衬衫 + 外套 + 围巾
- 汉堡:面包 + 肉饼 + 生菜 + 酱料
💻 Java 代码实现
场景:咖啡店
第一步:定义组件接口
/**
* 组件接口:饮料
*/
public interface Beverage {
String getDescription(); // 描述
double getCost(); // 价格
}
第二步:实现具体组件(基础咖啡)
/**
* 具体组件:美式咖啡
*/
public class Americano implements Beverage {
@Override
public String getDescription() {
return "美式咖啡";
}
@Override
public double getCost() {
return 15.0;
}
}
/**
* 具体组件:拿铁
*/
public class Latte implements Beverage {
@Override
public String getDescription() {
return "拿铁";
}
@Override
public double getCost() {
return 20.0;
}
}
/**
* 具体组件:卡布奇诺
*/
public class Cappuccino implements Beverage {
@Override
public String getDescription() {
return "卡布奇诺";
}
@Override
public double getCost() {
return 22.0;
}
}
第三步:定义装饰器抽象类
/**
* 装饰器抽象类
* 关键:继承 Beverage 并持有 Beverage 引用
*/
public abstract class CondimentDecorator implements Beverage {
protected 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() + 5.0;
}
}
/**
* 具体装饰器:糖
*/
public class Sugar extends CondimentDecorator {
public Sugar(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 糖";
}
@Override
public double getCost() {
return beverage.getCost() + 2.0;
}
}
/**
* 具体装饰器:奶泡
*/
public class MilkFoam extends CondimentDecorator {
public MilkFoam(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 奶泡";
}
@Override
public double getCost() {
return beverage.getCost() + 3.0;
}
}
/**
* 具体装饰器:巧克力
*/
public class Chocolate extends CondimentDecorator {
public Chocolate(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 巧克力";
}
@Override
public double getCost() {
return beverage.getCost() + 4.0;
}
}
第五步:使用装饰器
public class CoffeeShop {
public static void main(String[] args) {
// 1. 纯美式咖啡
Beverage coffee1 = new Americano();
System.out.println(coffee1.getDescription() + " = ¥" + coffee1.getCost());
// 2. 美式 + 牛奶
Beverage coffee2 = new Americano();
coffee2 = new Milk(coffee2);
System.out.println(coffee2.getDescription() + " = ¥" + coffee2.getCost());
// 3. 拿铁 + 糖 + 奶泡
Beverage coffee3 = new Latte();
coffee3 = new Sugar(coffee3);
coffee3 = new MilkFoam(coffee3);
System.out.println(coffee3.getDescription() + " = ¥" + coffee3.getCost());
// 4. 卡布奇诺 + 双倍巧克力 + 牛奶
Beverage coffee4 = new Cappuccino();
coffee4 = new Chocolate(coffee4);
coffee4 = new Chocolate(coffee4); // 双倍巧克力
coffee4 = new Milk(coffee4);
System.out.println(coffee4.getDescription() + " = ¥" + coffee4.getCost());
}
}
输出:
美式咖啡 = ¥15.0
美式咖啡 + 牛奶 = ¥20.0
拿铁 + 糖 + 奶泡 = ¥25.0
卡布奇诺 + 巧克力 + 巧克力 + 牛奶 = ¥35.0
🔥 实战案例:IO 流装饰器
Java IO 是装饰器模式的经典应用!
import java.io.*;
public class IODecoratorDemo {
public static void main(String[] args) throws Exception {
// 最基础的字节流
InputStream in = new FileInputStream("data.txt");
// 加一层缓冲功能
in = new BufferedInputStream(in);
// 再加一层数据读取功能
DataInputStream dataIn = new DataInputStream(in);
// 现在可以直接读取基本类型
int number = dataIn.readInt();
// 一层层装饰!
}
}
Java IO 的装饰器结构:
InputStream(抽象组件)
├── FileInputStream(具体组件)
├── ByteArrayInputStream(具体组件)
└── FilterInputStream(装饰器基类)
├── BufferedInputStream(具体装饰器)
├── DataInputStream(具体装饰器)
└── ...
🔥 实战案例:日志增强
/**
* 日志接口
*/
public interface Logger {
void log(String message);
}
/**
* 基础日志实现
*/
public class BasicLogger implements Logger {
@Override
public void log(String message) {
System.out.println(message);
}
}
/**
* 日志装饰器基类
*/
public abstract class LoggerDecorator implements Logger {
protected Logger logger;
public LoggerDecorator(Logger logger) {
this.logger = logger;
}
@Override
public void log(String message) {
logger.log(message);
}
}
/**
* 时间戳装饰器
*/
public class TimestampDecorator extends LoggerDecorator {
public TimestampDecorator(Logger logger) {
super(logger);
}
@Override
public void log(String message) {
String timestamp = java.time.LocalDateTime.now().toString();
logger.log("[" + timestamp + "] " + message);
}
}
/**
* 日志级别装饰器
*/
public class LevelDecorator extends LoggerDecorator {
private String level;
public LevelDecorator(Logger logger, String level) {
super(logger);
this.level = level;
}
@Override
public void log(String message) {
logger.log("[" + level + "] " + message);
}
}
/**
* 加密装饰器
*/
public class EncryptDecorator extends LoggerDecorator {
public EncryptDecorator(Logger logger) {
super(logger);
}
@Override
public void log(String message) {
// 简单加密示例
String encrypted = "ENCRYPTED[" + message.hashCode() + "]";
logger.log(encrypted);
}
}
使用方式:
public class LoggerDemo {
public static void main(String[] args) {
// 基础日志
Logger logger1 = new BasicLogger();
logger1.log("普通日志");
System.out.println();
// 加时间戳
Logger logger2 = new TimestampDecorator(new BasicLogger());
logger2.log("带时间戳的日志");
System.out.println();
// 加时间戳 + 日志级别
Logger logger3 = new LevelDecorator(
new TimestampDecorator(new BasicLogger()),
"INFO"
);
logger3.log("带级别的日志");
System.out.println();
// 加时间戳 + 级别 + 加密
Logger logger4 = new EncryptDecorator(
new LevelDecorator(
new TimestampDecorator(new BasicLogger()),
"ERROR"
)
);
logger4.log("敏感信息");
}
}
输出:
普通日志
[2024-01-15T10:30:45.123] 带时间戳的日志
[INFO] [2024-01-15T10:30:45.125] 带级别的日志
ENCRYPTED[-1234567890]
📊 类图结构
┌────────────────┐
│ Component │ ◄────────────────┐
│ + operation() │ │
└───────▲────────┘ │
│ │
┌───────────┴───────────┐ │
│ │ │
┌───┴────────┐ ┌───────┴───────┐ │
│ Concrete │ │ Decorator │ ──────┘
│ Component │ │ -component │ 持有引用
│+operation()│ │ +operation() │
└────────────┘ └───────▲───────┘
│
┌────────────┴────────────┐
│ │
┌───────┴───────┐ ┌───────┴───────┐
│ DecoratorA │ │ DecoratorB │
│ +operation() │ │ +operation() │
└───────────────┘ └───────────────┘
⚠️ 装饰器 vs 继承
| 对比项 | 继承 | 装饰器 |
|---|---|---|
| 扩展时机 | 编译时 | 运行时 |
| 灵活性 | 低 | 高 |
| 组合方式 | 固定 | 自由组合 |
| 类的数量 | 会爆炸 | 可控 |
✅ 适用场景
- 动态添加功能:在不修改原类的情况下扩展功能
- 功能可选:功能可以任意组合
- 代替继承:继承层次过深时
⚠️ 注意事项
- 装饰器和被装饰对象必须实现相同的接口
- 装饰器持有被装饰对象的引用
- 装饰顺序可能影响结果
小结
装饰器模式的核心:通过包装的方式,动态地给对象添加功能。
关键特点:
- 装饰器和被装饰者实现同一接口
- 装饰器持有被装饰者的引用
- 可以层层嵌套,像穿衣服一样
👉 下一篇:外观模式
