结构型设计模式之Decorator(装饰器)
结构型设计模式之Decorator(装饰器)
前言:
本案例通过李四举例,不改变源代码的情况下 对“才艺”进行增强。
摘要:
摘要: 装饰器模式是一种结构型设计模式,允许动态地为对象添加功能而不改变其源代码。本文通过李四的才艺展示案例演示了该模式的应用。关键点包括:1)意图是动态添加职责,比继承更灵活;2)结构包含Component、ConcreteComponent、Decorator和ConcreteDecorator四个角色;3)适用于需要透明扩展对象功能、支持功能撤销或无法使用子类扩展的场景。代码示例展示了如何通过DecoratorA和DecoratorB在原有"画画"才艺基础上逐步添加"唱歌"和"跳舞"功能,体现了装饰器模式的逐层增强特性。运行结果验证了功能的动态叠加效果。
1)意图
动态的给一个对象添加一些额外的职责。就增加功能而言,Decorator模式比生成子类更加灵活。
2)结构
其中:
- Component 定义一个对象接口,可以给这些对象动态的添加职责。
- ConcreteComponent 定义一个对象,可以给这个对象添加一些职责。
- Decorator 维持一个指向 Component 对象的指针,并定义一个与Component接口一致的接口。
- ConcreteDecorator 向组件添加职责。
3)适用性
Decorator 模式适用于:
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 处理那些可以撤销的职责。
- 当不能采用生成子类的方式进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是,由于定义被隐藏,或类定义不能用于生成子类。【了解】
4) 经典场景设计
1,在不修改现有对象结构的情况下扩展功能(核心场景):
当你需要给一个类添加新功能,但又不希望(或不能)修改这个类的源代码时(例如,类来自第三方库、是final类、修改风险高)。
当你需要给一个对象添加的功能是可选、可组合的,并且可能需要多种组合方式时。
例子: 给一个核心数据读写对象添加加密、压缩、缓存、日志记录、校验等功能。你可以自由组合这些装饰器(如 EncryptionDecorator(CompressionDecorator(CoreDataSource)))。
2,动态添加和撤销职责:
由于装饰器是组合关系,可以在运行时根据需要动态地给对象添加或移除装饰层。
例子: 一个文本编辑器中的文本格式化功能。基础 TextComponent 显示纯文本。你可以动态添加 BoldDecorator、ItalicDecorator、ColorDecorator 等来改变文本样式。移除装饰器即可撤销相应样式。
3,避免通过子类进行爆炸性扩展:
当功能组合的可能性非常多时,如果使用继承,需要为每一种可能的组合创建一个子类(如 EncryptedCompressedFile,CachedEncryptedFile,LoggedCachedFile 等),导致类数量急剧膨胀(“类爆炸”)。
装饰器模式通过组合,只需要定义每个独立功能的装饰器类,然后在运行时按需叠加,大大减少了类的数量。
例子: 图形用户界面(GUI)组件。基础组件如 SimpleWindow。功能如滚动条(ScrollbarDecorator)、边框(BorderDecorator)、标题栏(TitlebarDecorator)。你可以轻松创建带滚动条和边框的窗口(BorderDecorator(ScrollbarDecorator(SimpleWindow))),而无需创建 BorderedScrollableWindow 这样的特定子类。
4,为对象添加额外的行为,这些行为可能与核心逻辑正交:
有些行为(如日志记录、性能监控、权限检查、事务管理、缓存)是横切关注点,它们独立于对象的核心业务逻辑。
使用装饰器可以将这些横切关注点与核心逻辑解耦,使核心类更专注于自身职责,提高代码的模块化和可维护性。
例子: 在服务层或数据访问层接口上添加日志记录(LoggingServiceDecorator)或性能监控(TimingServiceDecorator)功能。核心 ConcreteService 实现业务逻辑,装饰器负责记录调用信息或耗时。
5,替代多重继承(在支持单继承的语言中尤其有用):
在Java、C#等单继承语言中,装饰器模式提供了一种模拟“拥有多个不同父类行为”的方式,通过组合叠加多个装饰器来实现。
例子: 一个游戏角色对象。基础角色有基本属性和行为。可以通过装饰器添加各种“Buff”或“Debuff”效果(如 SpeedBoostDecorator、InvisibleDecorator、PoisonedDecorator),这些效果可以叠加和移除。用继承很难优雅地实现这种动态、临时的效果叠加。
6,I/O流处理(最经典的实现之一):
Java的 java.io 包和 .NET的 System.IO 包是装饰器模式的教科书级应用。
基础组件如 FileInputStream / FileStream(读取字节/字节数组)。
装饰器如:
BufferedInputStream / BufferedStream:添加缓冲功能,提高效率。
DataInputStream:添加读取基本数据类型(int, float等)的功能。
GZIPInputStream / DeflateStream:添加解压缩功能。
ObjectInputStream:添加反序列化对象的功能。
你可以灵活组合:BufferedInputStream(FileInputStream) 提供缓冲的文件读取;DataInputStream(BufferedInputStream(FileInputStream)) 提供缓冲且能读基本数据类型的文件读取。
7,Web开发中的中间件/拦截器:
在Web框架(如Express.js/Koa.js, ASP.NET Core Middleware)中,处理HTTP请求/响应的管道(Pipeline)经常使用类似装饰器(或责任链+装饰)的思想。
每个中间件组件可以看作是一个装饰器,它接收一个代表核心处理逻辑(或下一个中间件)的“组件”,并在其前后添加自己的处理逻辑(如日志、认证、授权、异常处理、请求/响应转换、缓存)。
例子: AuthenticationMiddleware(AuthorizationMiddleware(LoggingMiddleware(CoreControllerHandler)))。
代码
/*** @author psd 结构性设计模式之装饰模式*/
public class DecoratorDemo {public static void main(String[] args) {Person lisi = new Student("lisi");lisi.operation();lisi = new DecoratorA(lisi);lisi.operation();System.out.println("-----------------------------");lisi = new DecoratorB(lisi);lisi.operation();}
}class DecoratorB extends Decorator {public DecoratorB(Person person) {this.person = person;}@Overridepublic void operation() {person.operation();System.out.println("跳舞");}
}class DecoratorA extends Decorator {public DecoratorA(Person person) {this.person = person;}@Overridepublic void operation() {person.operation();System.out.println("唱歌");}
}abstract class Decorator extends Person {protected Person person;
}class Student extends Person {public Student(String userName) {this.userName = userName;}@Overridepublic void operation() {System.out.println(userName + "才艺:画画");}
}abstract class Person {protected String userName;/*** 才艺描述*/public abstract void operation();
}
运行结果: