目录
[GUI 组件](#GUI 组件)
模式动机
一般有两种方式可以实现给一个类或对象增加行为:
- 继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。
- 关联机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)
装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。这就是装饰模式的模式动机。
模式定义
装饰(Decorator)模式是一种结构型设计模式,它允许在运行时动态地给一个对象添加新的职责或行为,而无需修改原有的类结构。装饰模式的核心思想是在不改变原有对象的基础上,通过组合的方式将新的职责附加到对象上。
模式结构
装饰模式包含以下几个主要角色:
- Component(组件接口):定义了被装饰对象的接口。
- ConcreteComponent(具体组件):实现了组件接口,定义了具体的组件对象。
- Decorator(装饰抽象类):也实现了组件接口,持有一个对组件对象的引用,并通过组合的方式将新的职责附加到组件对象上。
- ConcreteDecorator(具体装饰类):实现了装饰抽象类,提供了具体的装饰行为。
类图
代码分析
示例:动态添加功能的流
假设我们正在开发一个文本处理系统,需要支持多种文本处理功能,如加密、压缩等。我们可以使用装饰模式来实现这一需求。
组件接口
// 组件接口
interface Stream {
void write(String data);
}
具体组件
// 具体组件:文件流
class FileStream implements Stream {
@Override
public void write(String data) {
System.out.println("Writing data to file: " + data);
}
}
装饰抽象类
// 装饰抽象类
class StreamDecorator implements Stream {
protected Stream stream;
public StreamDecorator(Stream stream) {
this.stream = stream;
}
@Override
public void write(String data) {
stream.write(data);
}
}
具体装饰类
// 具体装饰类:加密流
class EncryptedStream extends StreamDecorator {
public EncryptedStream(Stream stream) {
super(stream);
}
@Override
public void write(String data) {
String encryptedData = encrypt(data);
super.write(encryptedData);
}
private String encrypt(String data) {
// 加密逻辑
return "Encrypted: " + data;
}
}
// 具体装饰类:压缩流
class CompressedStream extends StreamDecorator {
public CompressedStream(Stream stream) {
super(stream);
}
@Override
public void write(String data) {
String compressedData = compress(data);
super.write(compressedData);
}
private String compress(String data) {
// 压缩逻辑
return "Compressed: " + data;
}
}
客户端
public class DecoratorPatternDemo {
public static void main(String[] args) {
Stream stream = new FileStream();
Stream encryptedStream = new EncryptedStream(stream);
Stream compressedStream = new CompressedStream(encryptedStream);
compressedStream.write("Hello, World!");
}
}
模式分析
核心思想
装饰模式的核心思想是在不改变对象自身的基础上,通过组合的方式将新的职责附加到对象上。装饰模式允许在运行时动态地给一个对象添加新的职责或行为,而无需修改原有的类结构。
动态扩展功能
装饰模式通过装饰抽象类和具体装饰类,可以在运行时动态地扩展对象的功能。具体装饰类通过继承装饰抽象类,并重写其中的方法来添加新的行为。通过这种方式,可以在不修改原有对象的基础上,灵活地扩展对象的功能。
组合优于继承
装饰模式通过组合的方式扩展对象的功能,而不是通过继承。这种方式避免了继承带来的类爆炸性增长问题,并且使得系统更加灵活和可扩展。
优点
动态扩展功能
装饰模式允许在运行时动态地给一个对象添加新的职责或行为,而无需修改原有的类结构。通过装饰模式,可以在不改变对象自身的基础上,灵活地扩展对象的功能。
组合优于继承
装饰模式通过组合的方式扩展对象的功能,而不是通过继承。这种方式避免了继承带来的类爆炸性增长问题,并且使得系统更加灵活和可扩展。
代码复用性高
装饰模式通过组合的方式扩展对象的功能,可以复用已有的组件类和装饰类,从而提高代码的复用性。
符合开闭原则
装饰模式符合开闭原则,即对扩展开放,对修改关闭。通过引入装饰抽象类和具体装饰类,可以在不修改现有代码的情况下添加新的装饰类,从而扩展系统的功能。
缺点
增加系统的复杂性
装饰模式会增加系统的复杂性,因为它引入了额外的抽象类和具体装饰类。对于简单的系统,使用装饰模式可能会显得过于复杂。
类的膨胀
如果需要添加的装饰类很多,可能会导致装饰类的数量增加,从而增加系统的维护成本。
复杂的调试
由于装饰模式通过组合的方式扩展对象的功能,可能会导致对象的层次结构变得复杂,从而增加调试的难度。
适用环境
动态扩展功能
当需要在运行时动态地给一个对象添加新的职责或行为时,可以使用装饰模式。通过装饰模式,可以在不修改对象自身的基础上,灵活地扩展对象的功能。
避免继承带来的类爆炸性增长
当需要扩展对象的功能,但不想使用继承机制时,可以使用装饰模式。通过装饰模式,可以避免继承带来的类爆炸性增长问题,并且使得系统更加灵活和可扩展。
高度可定制化的需求
当需要高度可定制化的需求时,可以使用装饰模式。通过装饰模式,可以在运行时根据用户的需要动态地添加或移除对象的功能。
模式应用
输入输出流
在 Java 中,输入输出流的实现就使用了装饰模式。例如,BufferedInputStream
和 DataInputStream
都是 InputStream
的装饰类,它们通过组合的方式扩展了 InputStream
的功能。
InputStream inputStream = new FileInputStream("file.txt");
InputStream bufferedInputStream = new BufferedInputStream(inputStream);
InputStream dataInputStream = new DataInputStream(bufferedInputStream);
GUI 组件
在图形用户界面(GUI)中,组件的扩展也经常使用装饰模式。例如,一个按钮组件可以通过装饰模式添加新的功能,如添加背景颜色、边框等。
Button button = new Button("Click me");
Button decoratedButton = new BackgroundColorButton(button, Color.BLUE);
Button finalButton = new BorderButton(decoratedButton, BorderStyle.SOLID);
日志记录
在日志记录系统中,可以通过装饰模式动态地添加新的日志记录方式。例如,可以将日志记录到文件、数据库或网络服务器。
Logger logger = new FileLogger();
Logger decoratedLogger = new DatabaseLogger(logger);
Logger finalLogger = new NetworkLogger(decoratedLogger);
模式扩展
多层次装饰
在某些情况下,可能需要多层次的装饰。例如,一个文本处理系统可能需要支持多种文本处理功能,如加密、压缩、编码转换等。通过多层次的装饰,可以将这些不同的功能组合在一起,从而实现高度的灵活性和可扩展性。
Stream stream = new FileStream();
Stream encryptedStream = new EncryptedStream(stream);
Stream compressedStream = new CompressedStream(encryptedStream);
Stream encodedStream = new EncodedStream(compressedStream);
结合其他设计模式
装饰模式可以与其他设计模式结合使用,以进一步提高系统的灵活性和可扩展性。例如:
- 工厂模式:可以使用工厂模式来创建组件对象和装饰对象,从而进一步提高系统的灵活性。
- 策略模式:可以在装饰模式的基础上使用策略模式,动态地选择不同的装饰策略。
- 代理模式:可以在装饰模式的基础上使用代理模式,进一步增强对象的功能。
总结
- 装饰模式用于动态地给一个对象增加一些额外的职责,就增加对象功 能来说,装饰模式比生成子类实现更为灵活。它是一种对象结构型模 式。
- 装饰模式包含四个角色:抽象构件定义了对象的接口,可以给这些对 象动态增加职责(方法);具体构件定义了具体的构件对象,实现了 在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法); 抽象装饰类是抽象构件类的子类,用于给具体构件增加职责,但是具 体职责在其子类中实现;具体装饰类是抽象装饰类的子类,负责向构 件添加新的职责。
- 使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动 态地给一个对象附加更多的责任。装饰模式可以在不需要创造更多子 类的情况下,将对象的功能加以扩展。
- 装饰模式的主要优点在于可以提供比继承更多的灵活性,可以通过一种动态的 方式来扩展一个对象的功能,并通过使用不同的具体装饰类以及这些装饰类的 排列组合,可以创造出很多不同行为的组合,而且具体构件类与具体装饰类可 以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类;其主要缺 点在于使用装饰模式进行系统设计时将产生很多小对象,而且装饰模式比继承 更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需 要逐级排查,较为烦琐。
- 装饰模式适用情况包括:在不影响其他对象的情况下,以动态、透明的方式给 单个对象添加职责;需要动态地给一个对象增加功能,这些功能也可以动态地 被撤销;当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展 和维护时。
- 装饰模式可分为透明装饰模式和半透明装饰模式:在透明装饰模式中,要求客 户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该声明具体构 件类型和具体装饰类型,而应该全部声明为抽象构件类型;半透明装饰模式允 许用户在客户端声明具体装饰者类型的对象,调用在具体装饰者中新增的方法。