装饰器模式总结:动态(用组合代替继承,在运行时赋子类)地给一个对象增加一些额外的职责。
适用情况:过度地使用继承来扩展对象的功能(功能之间的组合也需要很多类来实现)导致子类的膨胀。
当一个父类下面有很多相似的子类,而且子类又衍生出很多子类,如:
Stream
生出子类:
FileStream/NetWorkStream/MemeryStream
这些子类,每个又需要添加一些功能,比如加密、缓冲等,又生出多个子类:
cryptoFileStream/BufferFileStream...
cryptoNetWorkStream/BufferNetWorkStream...
cryptoMemeryStream/BufferMemeryStream...
如果功能有很多的话,那么生出的子类就很多很多。但其实这些子类中有很多重复的代码(同样实现加密功能的类之间),它们之间不同的代码只是几句和stream类别有关的。
改进一下:让这些孙子类不是继承中间类,而是组合中间类,利用组合替代继承。将原来调用父类中的方法改为调用本类中成员变量(FileStream/NetWorkStream/MemeryStream等的对象)的方法。
再改进一下:我们发现这些类不同的地方都是由这个成员变量不同导致的,而这些成员变量都继承自一个类:Stream,那么将这些成员变量改为这个Stream,通过构造器,在运行的时候,通过向上传递,为这个Stream类型的成员变量赋不同的子类类型。做完了这一步我们发现实现相似功能的cryptoFileStream、cryptoNetWorkStream、cryptoMemeryStream的代码完全一致了,那么我们只需要写一个类cryptoStream。
再再改进一下:因为cryptoStream中依然有读写文件等操作,所以它需要继承Stream类,这一步之后,我们发现cryptoStream不仅继承了Stream,而且拥有一个Stream类型的成员变量,虽然这个成员变量在运行时会被指为它的子类。
再*3改进一下:需要添加功能的cryptoStream、BufferStream等有一个成员变量Stream,并且都继承自Stream,能不能将成员变量Stream提到Stream类中呢?但是同样继承自Stream的FileStream等类却不需要这个Stream变量。所以我们创建一个中间类:Decorator,它抽象了所有需要添加功能的类(继承自Sream,并且内置一个Stream,在构造函数中为这个Stream变量赋值),让cryptoStream、BufferStream等类继承Decorator类,所以最后的类图是这样的:
cryptoStream与BufferStream内置了一个Stream成员变量,这个成员变量会在运行时被赋值成他的子类FileStream或者NetworkStream等,所以上图cryptoStream看似和FileStream没什么关系,但都是通过这个内置的变量,实现“cryptoStream其实是在FileStream类的基础上扩展一些功能 ”这一功能。而Decorator类主要就是使其子类都有这个Sream变量。
Decorator类继承自Stream是为了规范接口,组合Stream是为了在运行时支持多态,调用Stream子类的方法。
装饰器模式总结:动态(用组合代替继承,在运行时赋子类)地给一个对象增加一些额外的职责。
其实装饰器模式并非为了解决子类衍生膨胀的问题,而是解决“主体类在多个方向上的扩展功能”的问题。因为其实FileStream、NetworkStream这些是Stream的一些子类别,但是加密、缓存却不是子类别,而是功能扩展,装饰器模式将主题与功能分开了。
设计模式利用多态将编译时的不同转化成运行时的不同,编译时相同。
我开始的想法是,如果需要添加一个功能,比如加密,我会写一个函数,具体这个函数为哪个类服务,通过形参传递才知道,这样为什么不行呢?
因为不能更改FileStream类,所以必须要继承FileSteam写一个子类,在子类写语句前调用这个函数,这样其实和一般的想法是一样的,没有减少一个类,只是将相同的语句提炼成一个函数,这样只是不用写很多重复代码。但是之所以有这么多的类,不是因为它们有相同的语句,而是它们有不同的语句,所以类的个数没有减少。
参考:https://www.bilibili.com/video/av24176315/?p=6