文章目录
- 示例代码
- 什么是装饰器模式?
- 装饰器模式中的四个角色
- 装饰器模式示例
- [装饰器模式 vs 继承](#装饰器模式 vs 继承)
- 装饰器模式的优势
- 装饰器模式的注意事项

示例代码
完整代码
bash
src/
├── common/ # 共享接口包
│ └── Coffee.java # Component:组件接口
│
└── decorator/ # 装饰器实现
├── SimpleCoffee.java # Concrete Component:具体组件
├── CoffeeDecorator.java # Base Decorator:基础装饰器
├── MilkDecorator.java # Concrete Decorator:牛奶装饰器
├── SugarDecorator.java # Concrete Decorator:糖装饰器
├── WhipDecorator.java # Concrete Decorator:奶泡装饰器
├── CoffeeShopClient.java # Client:客户端
└── DecoratorDemo.java # 运行入口
编译运行
bash
# 编译
javac -d out src/common/*.java src/decorator/*.java
# 运行
java -cp out decorator.DecoratorDemo
什么是装饰器模式?
概念
装饰器模式 是一种结构型设计模式,它的核心作用是动态地给对象添加一些额外的职责。装饰器让对象可以在运行时扩展功能,而不需要修改原有的类。
简单来说,装饰器就是在包装
生活类比
就像咖啡店的点单系统:
- 原味咖啡有基础价格和描述
- 可以加牛奶(+5元)
- 可以加糖(+2元)
- 可以加奶泡(+8元)
- 各种配料可以自由组合
在代码世界中,装饰器模式干的是同样的事:
- 基础组件提供了核心功能
- 装饰器可以在运行时包装组件,添加新功能
- 多个装饰器可以层层嵌套,组合出不同的效果
装饰器模式中的四个角色
Component(组件接口)
被装饰对象的接口。在我们的例子中是 Coffee:
java
// src/common/Coffee.java
package common;
public interface Coffee {
int getCost();
String getDescription();
}
客户端通过这个接口操作所有咖啡。
ConcreteComponent(具体组件)
实现组件接口的基础对象。在我们的例子中是 SimpleCoffee:
java
// src/decorator/SimpleCoffee.java
package decorator;
import common.Coffee;
public class SimpleCoffee implements Coffee {
@Override
public int getCost() {
return 10;
}
@Override
public String getDescription() {
return "原味咖啡";
}
}
这是最基础的咖啡,不带任何配料。
Decorator(装饰器)
持有一个组件引用,并实现组件接口。装饰器本身也是组件,所以可以递归嵌套:
java
// src/decorator/CoffeeDecorator.java
package decorator;
import common.Coffee;
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public int getCost() {
return coffee.getCost();
}
@Override
public String getDescription() {
return coffee.getDescription();
}
}
ConcreteDecorator(具体装饰器)
实际的装饰器实现,给组件添加新的行为:
java
// src/decorator/MilkDecorator.java
package decorator;
import common.Coffee;
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public int getCost() {
return super.getCost() + 5;
}
@Override
public String getDescription() {
return super.getDescription() + " + 牛奶";
}
}
装饰器模式示例
结构图
bash
客户端 --调用--> Coffee (Component)
↑
|
| implements
CoffeeDecorator (Decorator)
|
| 组合
↓
SimpleCoffee (ConcreteComponent)
运行示例
bash
>>> 点一杯原味咖啡
点单: 原味咖啡
价格: ¥10
>>> 原味咖啡 + 牛奶
点单: 原味咖啡 + 牛奶
价格: ¥15
>>> 原味咖啡 + 牛奶 + 糖
点单: 原味咖啡 + 牛奶 + 糖
价格: ¥17
>>> 原味咖啡 + 牛奶 + 糖 + 奶泡
点单: 原味咖啡 + 牛奶 + 糖 + 奶泡
价格: ¥25
代码实现
java
// 装饰器可以层层嵌套
Coffee coffee = new WhipDecorator(
new SugarDecorator(
new MilkDecorator(
new SimpleCoffee()
)
)
);
coffee.getDescription(); // "原味咖啡 + 牛奶 + 糖 + 奶泡"
coffee.getCost(); // 25
装饰器模式 vs 继承
| 特性 | 继承 | 装饰器 |
|---|---|---|
| 扩展时机 | 编译时 | 运行时 |
| 组合灵活性 | 静态的,不易改变 | 动态可组合,任意组合 |
| 类数量 | 每种组合都需要一个新类 | 只需组件类 + 装饰器类 |
| 开闭原则 | 违反(修改现有类) | 符合(扩展新功能无需修改现有类) |
装饰器模式的优势
- 运行时扩展:可以在程序运行期间动态添加装饰
- 自由组合:多种配料可以任意组合,牛奶+糖+奶泡
- 开闭原则:对扩展开放,对修改封闭
- 单一职责:每个装饰器只关心自己添加的那部分功能
- 可复用性:装饰器之间相互独立,可单独复用
装饰器模式的注意事项
- 装饰器链不要太长:嵌套层次过多会导致调试困难
- 顺序敏感:不同的添加顺序会产生不同的结果
- 类型检查:需要注意被装饰对象的真实类型