装饰者模式(Decorator Pattern)用于向对象添加附加的功能。
问题提出
假设你加入了一家提供云主机服务的公司,你的工作是设计类实现将数据存储到云端。
创建类CloudStream,它有write方法。
明天,我们需要在将敏感数据比如信用卡数据存入云端前进行加密。
我们不想修改通用的CloudStream类,因为加密数据只在特定的场合下才需要,正常情况下CloudStream工作得很好。我们如何处理这个问题?
可以新建一个EncryptedCloudStream类继承于CloudStream类,并重写write方法。在重写的write方法里,先调用私有方法encrypt对数据进行加密,然后调用父类的write方法进行存储数据。
再到main进行测试,分别创建一个CloudStream对象和EncryptedCloudStream对象,然后进行存储操作。
我们看到,CloudStream对象的write方法存储的是明文,而EncryptedCloudStream对象的write方法则会先加密再存储密文,目前的实现不错。
明天我们可能需要在数据存储前进行压缩,比如对于视频数据就需要这么处理。
类似EncryptedCloudStream类,我们创建CompressedCloudStream类,它继承CloudStream类,在重写的write方法里,先调用私有方法compress再调用父类的write进行存储。
问题出现了。如果我们需要对数据既进行加密又进行压缩,该如何处理?目前我们的实现不能解决这个问题。
我们必须创建一个类比如命名CompressedAndEncryptedCloudStream,处理加密和压缩的组合,显然这不是一个容易维护的技术路线。
只有加密和压缩两种操作还能接受,实际场景可能会有多个操作,我们需要组合各种可能的操作并实现相应的类,这是不能接受的。
解决的方法就是使用装饰者模式(Decorator Pattern),利用它我们可以给对象添加额外的行为,比如可以给CloudStream对象添加加密、日志或压缩等行为。
解决方案
当前是继承的结构,我们利用它来支持各种情形的云端存储。
这种方法很不灵活,也很难维护,当我们添加其他行为比如添加日志记录(logged)的话,就要添加多个继承类来应对各种可能的组合场景。
以前文章我们说过:优先使用组合而不是继承。
我们来看看如何实现。
引入接口Stream,它定义了write方法。CloudStream类和Encrypted类分别实现该接口。
你可能会问,这有什么区别?
区别在于,实现Stream接口的Encrypted类的write方法中,不会调用基类的write方法,而之前是需要调用基类的write方法的。
接着,我们使用组合方法在Encrypted类中添加一个私有Stream对象。在Encrypted的write方法里,先对数据进行加密,再将加密的数据传给Stream对象的write方法。
这就是装饰者模式(Decorator Pattern)。
我们可以给现存的对象中添加其他行为。Encrypted类就是一个修饰器,因为它修饰了CloudStream对象,给它添加了加密行为。
四人帮的图显然抽象一些。它定义了Component接口及其operation方法,ConcreteComponent实现了Component接口,Decorator则是装饰者,给ConcreteComponent添加额外的行为。
代码实现
装饰者模式的代码很简单。
先创建Stream接口,并定义write方法。
修改基本的CloudStream类,它实现了Stream接口。
修改CompressedCloudStream类,它也实现Stream接口,再添加stream属性,并在构造函数中初始化。在write方法里,实现该类的压缩功能后,再委托给stream的write方法。
同样的,我们对EncryptedCloudStream也作同样的修改。
再到main里进行测试。
创建一个静态函数storeCreditCard,用于存储信用卡数据。然后在main方法里调用storeCreditCard。
我们看到,可以给CloudStream添加压缩行为,也可以添加加密行为,甚至是多个行为的组合,非常灵活。这里的重点是,storeCreditCard根本无需知道加密、压缩或记录日志等其他的行为,它只需要Stream对象即可。
小结
装饰者模式(Decorator Pattern)可以向对象添加附加的功能。