Java 中的装饰器模式:动态扩展类
大约 4 分钟
也称为
- 智能代理
- 包装器
装饰器设计模式的意图
装饰器模式允许在不修改现有代码的情况下,动态地为对象添加职责。它通过提供一种将对象“包装”在具有相似接口的对象中的方式来实现这一点,从而增强了 Java 设计模式的灵活性。
装饰器模式的详细解释以及现实世界示例
现实世界示例
想象一家咖啡馆,在那里你可以定制你的咖啡订单。从一杯基本咖啡开始,你可以添加不同的配料,如牛奶、糖、奶油等。每个添加就像装饰器设计模式中的装饰器一样。基本咖啡对象可以动态地用额外的功能(口味、配料)进行装饰。例如,你可以从一个简单的咖啡对象开始,然后用牛奶装饰器包装它,接着用糖装饰器包装,最后用奶油装饰器包装。每个装饰器都会添加新功能或修改咖啡对象的行为,这类似于装饰器模式在软件设计中的工作方式。
通俗地说
装饰器模式允许你在运行时通过将对象包装在一个装饰器类的对象中来动态地改变对象的行为。
维基百科说
在面向对象编程中,装饰器模式是一种设计模式,允许在不影响同一类中其他对象行为的情况下,静态或动态地为单个对象添加行为。装饰器模式通常用于遵循单一职责原则,因为它允许将功能划分为具有独特关注领域的不同类,以及遵循开闭原则,通过允许扩展类的功能而无需修改它。
Java 中装饰器模式的编程示例
在附近的山丘里住着一只愤怒的巨魔。它通常赤手空拳,但有时会使用武器。为了武装巨魔,不需要创建新的巨魔,而是可以用合适的武器动态地装饰它。
首先,我们有一个实现 Troll
接口的 SimpleTroll
。
public interface Troll {
void attack();
int getAttackPower();
void fleeBattle();
}
@Slf4j
public class SimpleTroll implements Troll {
@Override
public void attack() {
LOGGER.info("The troll tries to grab you!");
}
@Override
public int getAttackPower() {
return 10;
}
@Override
public void fleeBattle() {
LOGGER.info("The troll shrieks in horror and runs away!");
}
}
接下来,我们要为巨魔添加一根棍棒。我们可以使用装饰器动态地做到这一点。
@Slf4j
@RequiredArgsConstructor
public class ClubbedTroll implements Troll {
private final Troll decorated;
@Override
public void attack() {
decorated.attack();
LOGGER.info("The troll swings at you with a club!");
}
@Override
public int getAttackPower() {
return decorated.getAttackPower() + 10;
}
@Override
public void fleeBattle() {
decorated.fleeBattle();
}
}
这是巨魔的实际操作。
public static void main(String[] args) {
// simple troll
LOGGER.info("A simple looking troll approaches.");
var troll = new SimpleTroll();
troll.attack();
troll.fleeBattle();
LOGGER.info("Simple troll power: {}.\n", troll.getAttackPower());
// change the behavior of the simple troll by adding a decorator
LOGGER.info("A troll with huge club surprises you.");
var clubbedTroll = new ClubbedTroll(troll);
clubbedTroll.attack();
clubbedTroll.fleeBattle();
LOGGER.info("Clubbed troll power: {}.\n", clubbedTroll.getAttackPower());
}
程序输出
11:34:18.098 [main] INFO com.iluwatar.decorator.App -- A simple looking troll approaches.
11:34:18.100 [main] INFO com.iluwatar.decorator.SimpleTroll -- The troll tries to grab you!
11:34:18.100 [main] INFO com.iluwatar.decorator.SimpleTroll -- The troll shrieks in horror and runs away!
11:34:18.100 [main] INFO com.iluwatar.decorator.App -- Simple troll power: 10.
11:34:18.100 [main] INFO com.iluwatar.decorator.App -- A troll with huge club surprises you.
11:34:18.101 [main] INFO com.iluwatar.decorator.SimpleTroll -- The troll tries to grab you!
11:34:18.101 [main] INFO com.iluwatar.decorator.ClubbedTroll -- The troll swings at you with a club!
11:34:18.101 [main] INFO com.iluwatar.decorator.SimpleTroll -- The troll shrieks in horror and runs away!
11:34:18.101 [main] INFO com.iluwatar.decorator.App -- Clubbed troll power: 20.
何时在 Java 中使用装饰器模式
装饰器用于
- 动态且透明地为单个对象添加职责,即不会影响其他对象,这是 Java 设计模式的关键特性。
- 对于可以撤回的职责。
- 当扩展类由于可能导致的子类泛滥而不切实际时。
- 当类定义可能隐藏或不可用于子类化时。
装饰器模式 Java 教程
装饰器模式在 Java 中的现实世界应用
- GUI 工具包经常使用装饰器来动态地为组件添加滚动、边框或布局管理等行为。
- Java 中的 java.io.InputStream、java.io.OutputStream、java.io.Reader 和 java.io.Writer 类是利用装饰器模式的知名示例。
- java.util.Collections#synchronizedXXX()
- java.util.Collections#unmodifiableXXX()
- java.util.Collections#checkedXXX()
装饰器模式的优缺点
优点
- 比静态继承更灵活。
- 避免了层次结构中较高位置的特性繁多的类,展示了 Java 设计模式的强大功能。
- 装饰器与其组件并不相同。
- 可以在运行时添加或删除职责。
权衡
- 装饰器与其组件并不相同,因此对对象类型的测试将失败。
- 装饰器会导致系统拥有大量看起来相同的较小对象,这使得程序员难以实现所需的配置。
- 过度使用会导致代码结构变得复杂,因为引入了许多较小的类。
相关的 Java 设计模式
- 适配器:装饰器更改对象的职责,而适配器更改对象的接口。
- 组合:装饰器可以被视为一个仅包含一个组件的退化组合。但是,装饰器会添加额外的职责——它并非用于对象聚合。
- 策略:装饰器允许你更改对象的皮肤,而策略允许你更改对象的内在。