装饰器模式
什么是装饰器模式?
装饰器模式是一种结构化设计模式,可以动态地为对象附加额外的行为。装饰器通过组合而非子类化(继承)提供了灵活的扩展能力。
何时使用它?
当您有许多可能的组合来构造对象时,请使用装饰器模式。
问题
假设我们正在为一家冰淇淋店开发一个系统。这家店有各种冰淇淋和配料。系统需要显示冰淇淋的描述(包括配料)和价格。
我们的第一个版本系统是这样的:
这看起来不错,但如果我们可以添加配料呢?
我们的第二个版本:
问题揭晓了!注意,即使它们还不够,因为我们还需要ChocolateIceCreamWithChocolateChipsAndMapleNuts
等等。
解决方案
装饰器模式又称为包装器模式,因为它通过为对象包装额外的功能来实现。被包装的对象(冰淇淋)被称为组件,而包装器对象(冰淇淋顶部)被称为装饰器。
-
IceCream 类
组件和装饰器具有通用的接口IceCream
类(稍后您将看到原因),它们都声明为IceCream
对象。 -
具体的冰淇淋类别
每种具体的冰淇淋都会覆盖成本方法,因为每种冰淇淋的价格都不同。 -
Topping 类
Topping 类为具体的配料提供接口并保存对IceCream
对象的引用。 -
具体的配料类
如果系统需要另一种配料,比如焦糖源,您需要做的只是创建CaramelSource
扩展Topping
类的类。
结构
装饰器类使用组合和继承,理解它们的意图至关重要。
在装饰器模式中,我们对组件和装饰器使用相同的类型。装饰器组合组件对象以获取行为,即获取组件对象中定义的字段或方法。而装饰器继承(扩展)组件,因此装饰器对象可以声明为组件对象。
装饰器模式遵循开放-封闭原则,即对扩展开放,对修改关闭。添加组件或装饰器非常简单。例如,如果要添加另一个具体的装饰器,只需创建一个代表它的类并扩展 Decorator 类即可。
Java 中的实现
// Component class
public abstract class IceCream {
public String description = "Unknown ice cream";
public String getDescription() {
return description;
}
public abstract double cost();
}
// Concrete component class
public class ChocolateIceCream extends IceCream {
public ChocolateIceCream() {
description = "ChocolateIceCream";
}
@Override
public double cost() {
return 1.99;
}
}
// Base decorator class
public abstract class Topping extends IceCream {
public IceCream iceCream;
// All subclasses (concrete decorator classes) need to implement getDescription method,
// by declaring this method as abstract, we enforce all subclasses to implement this method
public abstract String getDescription();
}
// Concrete decorator class
public class MapleNuts extends Topping {
public MapleNuts(IceCream iceCream) {
this.iceCream = iceCream;
}
@Override
public String getDescription() {
return iceCream.getDescription() + ", MapleNuts";
}
@Override
public double cost() {
return iceCream.cost() + .30;
}
}
// Concrete decorator class
public class PeanutButterShell extends Topping {
public PeanutButterShell(IceCream iceCream) {
this.iceCream = iceCream;
}
@Override
public String getDescription() {
return iceCream.getDescription() + ", PeanutButterShell";
}
@Override
public double cost() {
return iceCream.cost() + .30;
}
}
public class Client {
public static void main(String[] args) {
IceCream iceCream = new ChocolateIceCream();
System.out.println(iceCream.getDescription() + ", $" + iceCream.cost());
iceCream = new MapleNuts(iceCream);
System.out.println(iceCream.getDescription() + ", $" + iceCream.cost());
iceCream = new PeanutButterShell(iceCream);
System.out.println(iceCream.getDescription() + ", $" + iceCream.cost());
}
}
输出:
ChocolateIceCream, $1.99
ChocolateIceCream, MapleNuts, $2.29
ChocolateIceCream, MapleNuts, PeanutButterShell, $2.59
陷阱
- 通常会导致大量的类被添加到系统中,其中每个类都提供小的行为。
- 如果要让每个装饰器都了解内部装饰器,则需要进行一些额外的工作。在上面的例子中,如果您想打印
ChocolateIceCream, MapleNuts * 2
而不是打印ChocolateIceCream, MapleNuts, MapleNuts
,则每个装饰器都需要知道到目前为止添加了哪些配料(例如,可以通过 ArrayList 来实现)。
您可以在这里查看所有设计模式的实现。GitHub
仓库
附言:
我是科技博客的新手,如果你有什么改进建议,或者有什么困惑的地方,请留言!
感谢阅读 :)