给代码穿上品如的衣服:装饰器模式的套娃艺术
一、当继承开始「套娃」
你是否经历过这样的崩溃?
想给咖啡加奶加糖,却被迫创建了SugarMilkCoffee
、SugarOnlyCoffee
等48个子类;
要给游戏角色装备皮肤,结果代码里长出了RedHatBlueSwordArmorHero
这样的类名...
这时你需要装饰器模式------它像品如的衣柜,让对象可以随时随地换皮肤,还能把十层Buff叠得比千层饼还优雅。
二、俄罗斯套娃的设计图(UML图)
java
┌─────────────┐
│ Component │
├─────────────┤
│ +operation()│
└──────△──────┘
│
┌─────────────┴─────────────┐
│ │
┌─────────────┐ ┌─────────────┐
│ Concrete │ │ Decorator │
│ Component │ ├─────────────┤
└─────────────┘ │ -component │
│ +operation()│
└──────△──────┘
│
┌────────────┴────────────┐
│ │
┌─────────────┐ ┌─────────────┐
│ Concrete │ │ Concrete │
│ DecoratorA │ │ DecoratorB │
└─────────────┘ └─────────────┘
- 素颜女神(Component):定义核心功能
- 基础款(ConcreteComponent):实现基础功能
- 美妆博主(Decorator):持有组件引用,提供增强接口
- 彩妆单品(ConcreteDecorator):具体增强实现
三、咖啡店の魔法时刻(代码实战)
1. 基础咖啡(素颜版)
java
// 咖啡接口(Component)
interface Coffee {
String getDesc();
double cost();
}
// 基础咖啡(ConcreteComponent)
class SimpleCoffee implements Coffee {
public String getDesc() { return "咖啡"; }
public double cost() { return 8.0; }
}
2. 魔法装饰器(美妆版)
java
// 装饰器基类(Decorator)
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
public String getDesc() {
return decoratedCoffee.getDesc();
}
public double cost() {
return decoratedCoffee.cost();
}
}
// 摩卡魔法(具体装饰器)
class MochaDecorator extends CoffeeDecorator {
public MochaDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDesc() {
return super.getDesc() + "+摩卡";
}
@Override
public double cost() {
return super.cost() + 3.0;
}
}
// 香草咒语(具体装饰器)
class VanillaDecorator extends CoffeeDecorator {
public VanillaDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDesc() {
return super.getDesc() + "+香草";
}
@Override
public double cost() {
return super.cost() + 2.5;
}
}
3. 点一杯魔法咖啡
java
Coffee myCoffee = new SimpleCoffee();
myCoffee = new MochaDecorator(myCoffee); // 叠加摩卡
myCoffee = new VanillaDecorator(myCoffee);// 叠加香草
System.out.println(myCoffee.getDesc()); // 咖啡+摩卡+香草
System.out.println("价格:" + myCoffee.cost()); // 13.5元
四、装饰器 vs 继承:美颜相机 vs 整容医院
维度 | 装饰器模式 | 继承 |
---|---|---|
扩展方式 | 动态组合 | 静态编译 |
类数量 | 线性增长(N+M) | 指数爆炸(M^N) |
灵活性 | 运行时自由搭配 | 编译时确定 |
代码复用 | 装饰器可重复使用 | 每个组合都是新类 |
现实类比 | 乐高积木 | 石膏雕像 |
选型指南:
- 需要动态增减功能 → 装饰器
- 功能组合固定 → 继承
- 不确定时 → 选装饰器保平安
五、现实世界的叠Buff艺术
-
Java IO流:
java// 经典套娃三连 new BufferedReader( new InputStreamReader( new FileInputStream("data.txt") ) );
-
Web中间件:身份验证+日志记录+缓存装饰器链
-
游戏装备系统:武器基础属性+宝石强化+皮肤特效
-
前端高阶组件:Redux的connect()装饰React组件
-
Spring AOP:通过装饰器模式实现切面增强
冷知识 :
Java的Collections.unmodifiableList()
其实是个装饰器,给集合套上「只读盔甲」。
六、防套娃翻车指南
-
控制嵌套层数:
java// 错误示范:套娃十连击 coffee = new Mocha(new Milk(new Sugar(...(new SimpleCoffee()))));
-
保持装饰器透明:
java// 好的装饰器应该像透明滤镜 // 不要偷偷修改对象核心状态
-
避免循环装饰:
java// 禁止:装饰器A装饰装饰器B,B又装饰A // 这会导致代码进入莫比乌斯环
-
优先接口装饰:
java// 装饰器应该面向接口 // 而不是具体实现类
-
警惕性能损耗:
java// 每个装饰器都是包装盒 // 套太多会影响性能
七、品如の衣柜总结大会
装饰器模式就像代码界的奇迹暖暖:
- ✅ 要:用于动态增强对象功能
- ✅ 要:保持装饰器单一职责
- ❌ 不要:把装饰器当继承用
- ❌ 不要:忘记装饰器带来的复杂度
当你在Java IO流中看到层层嵌套时,请向这些优雅的装饰器致敬------正是它们让简单的字节流变成了强大的处理管道!