2.5 装饰器模式
装饰器模式(Decorator)让你在运行时给一个对象动态叠加职责,而不修改它的类、也不影响同类的其它对象。它是“组合优于继承”最优雅的体现之一。
设想一杯咖啡:基础是浓缩,可以加牛奶、加摩卡、加奶油。如果用继承表达每种组合,你会得到 MilkMochaWhipEspresso 这种荒谬的类,而且 N 种配料会产生 2^N 个子类。装饰器换一种思路:每种配料是一个“包装器”,套在饮品外面,叠加价格与描述。
下面的加料台:每勾选一种配料,就多包一层装饰器,实时看到嵌套的 new Whip(new Milk(new Espresso())) 和最终价格。
正在加载交互实验...
装饰器与被装饰者共享接口
java
interface Beverage { double cost(); }
class Espresso implements Beverage {
public double cost() { return 18; }
}
// 抽象装饰器:实现同一接口,并组合一个 Beverage
abstract class AddOn implements Beverage {
protected Beverage inner;
AddOn(Beverage inner) { this.inner = inner; }
}
class Milk extends AddOn {
Milk(Beverage inner) { super(inner); }
public double cost() { return inner.cost() + 3; } // 叠加
}因为装饰器本身也是 Beverage,所以它能被再次装饰,层层嵌套。
顺序有时很重要
当装饰器有副作用或会转换数据时,叠加顺序会改变结果。Java 的 IO 流就是典型:new BufferedReader(new InputStreamReader(...))。先压缩再加密 和 先加密再压缩,产出完全不同。下面的堆叠器让你给“压缩 / 加密 / Base64”三层装饰排序,观察 write() 的包裹方向如何变化。
正在加载交互实验...
正在加载概念检查...
装饰器 vs 继承 vs 代理
- vs 继承:继承在编译期固定,装饰器在运行时组合。需要“任意组合、动态叠加”时选装饰器。
- vs 代理:两者结构几乎一样(都包裹一个对象、都实现同接口),区别在意图。装饰器是为了增强功能;代理是为了控制访问(懒加载、鉴权、远程)。
正在加载概念检查...
正在加载本节练习...