设计模式笔记(十四)– 装饰者模式

装饰者模式(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)可以向对象添加附加的功能。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注