设计模式 学习笔记 之 装饰模式 Decorator(6)
2017-12-06 18:47
627 查看
之前的学习过程中 学习了单一职责类:
在软件组件设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求变化,子类极具膨胀,同时充斥着重复代码,
这时候关键就是划清责任。
今天就具体学习下单一职责中的装饰模式。
动机:在某些情况下我们可能会“过度的使用继承来拓展对象的功能”,由于继承为类型引入的静态特质,是的这种扩展
方式缺乏灵活性,并且随着子类的增多(扩展的功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
如何使“对象功能的拓展”能够根据需要来动态的实现?同时便面“扩展功能的增多”带来的子类膨胀问题?
从而使得任何“功能扩展的变化”所导致的影响降低!
其实java的IO 处理就是装饰模式实现的。
接下来我们就用C++ 伪代码的 设计我们自己的 IO 代码,按照常规思路去设计的话 首先我们先来抽象接口
然后通过不同的实体进行子类话,伪代码如下
当新的需求来了现在 需要加密的时候文件流,还需要缓冲流:
用了继承的方式 当新的需求去改变流的时候将会不停的继承成子类。在没有学习装饰模式之前,和容易掉进这样的陷阱。
假设我们现在要有个既有加密又有缓冲的流操作 :
就会又多出三个子类去继承 来张图:
来算下 如果这样设计下来会存在多少个 类呢 1 +n +n *m! /2 现在的 n = 2 ,m= 3; 如果n 和m 都变大了 ,
那真的是个可怕的事情。
既然知道这是个非常可怕,并将来的对维护而言肯定更加困难,
根据设计原则 组合优于继承 我们将代码重构
有多态意识的可以意识到 使用组合关系的时候使用的全是子类 当大部分变量是某个类型的的子类,就不用声明成子类 直接声明父类就好
也就是里氏替换原则,
我们可以把编译期的帮绑定替换成运行时的 。
到这里以后来一次总结 : 我们可以发现继承和组合的微妙关系 ,编译时复用,运行时去真的变化。
这也是我们写面向对象代码的真谛 。
仔细来看上面的代码是不是发现竟然是一样的。不一样的在哪里 就是在那个未来 多态去支持变化的。
根据重构理论:如果某一个类有相同的子类就继续往上提
通过我们重构以后
他有多少类呢 ? 1+n+1+m
为什么会有怎么大的变化能 就是因为对继承的不良使用 ,这也就是我们的动机。继承引入的静态特质,组合关系引入动态。
通过组合的关系进行支持未来的变化(多态);
GOF 中的装饰模式的定义:
动态(组合)的给一个对象增加一些额外的职责,就增加功能而言,Decorator模式比生成子类(继承)更为灵活,
(消除重复代码&减少子类个数)。
总结:
通过采用组合的而非继承的方式,Decorator模式实现了在运行是动态扩展的功能,而且根据需求扩展多个功能。
避免了使用继承带来的“灵活性”和“子类衍生”问题。
Decorator类设计接口上表现位is-a Componet 继承关系。但是实际上为has-a Componetd的组合关系。
在代码中一旦看到这样的关系 ,我们就可以按照Decorator模式来处理。
在软件组件设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求变化,子类极具膨胀,同时充斥着重复代码,
这时候关键就是划清责任。
今天就具体学习下单一职责中的装饰模式。
动机:在某些情况下我们可能会“过度的使用继承来拓展对象的功能”,由于继承为类型引入的静态特质,是的这种扩展
方式缺乏灵活性,并且随着子类的增多(扩展的功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
如何使“对象功能的拓展”能够根据需要来动态的实现?同时便面“扩展功能的增多”带来的子类膨胀问题?
从而使得任何“功能扩展的变化”所导致的影响降低!
其实java的IO 处理就是装饰模式实现的。
接下来我们就用C++ 伪代码的 设计我们自己的 IO 代码,按照常规思路去设计的话 首先我们先来抽象接口
然后通过不同的实体进行子类话,伪代码如下
class Stream { // 定义一组功能操作接口 public : virtual char Read(int number) = 0; //读取 virtual void Seek(int positon) = 0;//定位 virtual void Write(char date) = 0; //写 virtual ~Stream(){ } }; class FileStream:public Stream{ //文件流处理类 public : virtual char Read(int number) { } virtual void Seek(int positon) { } virtual void Write(char date) { } }; class NetworkStrrea: public Stream{ //网络流 public : virtual char Read(int number) { } virtual void Seek(int positon) { } virtual void Write(char date) { } }; class MemoryStream: public Stream{ //内存流 public : virtual char Read(int number) { } virtual void Seek(int positon) { } virtual void Write(char date) { } };
当新的需求来了现在 需要加密的时候文件流,还需要缓冲流:
class CrytoFileStream :public FileStream { public : virtual char Read(int number) { //额外的加密操作…… FileStream ::Read(number); } virtual void Seek(int positon) { //额外的加密操作…… FileStream ::Seek(positon); } virtual void Write(char date) { //额外的加密操作…… FileStream ::Write(date); } }; class CrytoNetworkStrrea :public NetworkStrrea { public : virtual char Read(int number) { //额外的加密操作…… NetworkStrrea ::Read(number); } virtual void Seek(int positon) { //额外的加密操作…… NetworkStrrea ::Seek(positon); } virtual void Write(char date) { //额外的加密操作…… NetworkStrrea ::Write(date); } }; class CrytoMemoryStream :public MemoryStream { public : virtual char Read(int number) { //额外的加密操作…… MemoryStream ::Read(number); } virtual void Seek(int positon) { //额外的加密操作…… MemoryStream ::Seek(positon); } virtual void Write(char date) { //额外的加密操作…… MemoryStream ::Write(date); } }; class BufferFileStream :public FileStream{ //... }; class BufferNetworkStrrea :public NetworkStrrea{ //... }; class BufferMemoryStream :public MemoryStream{ //... };
用了继承的方式 当新的需求去改变流的时候将会不停的继承成子类。在没有学习装饰模式之前,和容易掉进这样的陷阱。
假设我们现在要有个既有加密又有缓冲的流操作 :
class CrytoBufferFileStream :public FileStream{ //... }; class CrytoBufferNetworkStrrea :public NetworkStrrea{ //... }; class CrytoBufferMemoryStream :public MemoryStream{ //... };
就会又多出三个子类去继承 来张图:
来算下 如果这样设计下来会存在多少个 类呢 1 +n +n *m! /2 现在的 n = 2 ,m= 3; 如果n 和m 都变大了 ,
那真的是个可怕的事情。
既然知道这是个非常可怕,并将来的对维护而言肯定更加困难,
根据设计原则 组合优于继承 我们将代码重构
class CrytoFileStream { FileStream *fileStream; public : virtual char Read(int number) { //额外的加密操作…… fileStream->Read(number); } virtual void Seek(int positon) { //额外的加密操作…… fileStream->Seek(positon); } virtual void Write(char date) { //额外的加密操作…… fileStream->Write(date); } }; class CrytoNetworkStrrea { NetworkStrrea *networkStrrea; public : virtual char Read(int number) { //额外的加密操作…… networkStrrea->Read(number); } virtual void Seek(int positon) { //额外的加密操作…… networkStrrea->Seek(positon); } virtual void Write(char date) { //额外的加密操作…… networkStrrea->Write(date); } }; class CrytoMemoryStream { MemoryStream *memoryStream; public : virtual char Read(int number) { //额外的加密操作…… memoryStream->Read(number); } virtual void Seek(int positon) { //额外的加密操作…… memoryStream->Seek(positon); } virtual void Write(char date) { //额外的加密操作…… memoryStream->Write(date); } };
有多态意识的可以意识到 使用组合关系的时候使用的全是子类 当大部分变量是某个类型的的子类,就不用声明成子类 直接声明父类就好
也就是里氏替换原则,
我们可以把编译期的帮绑定替换成运行时的 。
class CrytoFileStream { Stream *stream; //new FileStream(); public : virtual char Read(int number) { //额外的加密操作…… stream->Read(number); } virtual void Seek(int positon) { //额外的加密操作…… stream->Seek(positon); } virtual void Write(char date) { //额外的加密操作…… stream->Write(date); } }; class CrytoNetworkStrrea { Stream *stream; public : virtual char Read(int number) { //额外的加密操作…… stream->Read(number); } virtual void Seek(int positon) { //额外的加密操作…… stream->Seek(positon); } virtual void Write(char date) { //额外的加密操作…… stream->Write(date); } }; class CrytoMemoryStream { Stream *stream; public : virtual char Read(int number) { //额外的加密操作…… stream->Read(number); } virtual void Seek(int positon) { //额外的加密操作…… stream->Seek(positon); } virtual void Write(char date) { //额外的加密操作…… stream->Write(date); } };
到这里以后来一次总结 : 我们可以发现继承和组合的微妙关系 ,编译时复用,运行时去真的变化。
这也是我们写面向对象代码的真谛 。
仔细来看上面的代码是不是发现竟然是一样的。不一样的在哪里 就是在那个未来 多态去支持变化的。
class CrytoStream :public Stream { //为什么要继承呢?因为需要接口的规范 Stream *stream; public : virtual char Read(int number) { //额外的加密操作…… stream->Read(number); } virtual void Seek(int positon) { //额外的加密操作…… stream->Seek(positon); } virtual void Write(char date) { //额外的加密操作…… stream->Write(date); } }; class BufferStream :public Stream{ //同理 我们也可以得到一个bufferSteam 缓冲流 };
根据重构理论:如果某一个类有相同的子类就继续往上提
class DecoratorStream :public Stream { Stream * stream; }; class CrytoStream :public DecoratorStream { public: CrytoStream(Stream * stream):DecoratorStream(stream){ } public : virtual char Read(int number) { //额外的加密操作…… stream->Read(number); } virtual void Seek(int positon) { //额外的加密操作…… stream->Seek(positon); } virtual void Write(char date) { //额外的加密操作…… stream->Write(date); } }; class BufferStream :public DecoratorStream{ //同理 我们也可以得到一个bufferSteam 缓冲流 public: BufferStream(Stream * stream):DecoratorStream(stream){ } };
通过我们重构以后
他有多少类呢 ? 1+n+1+m
为什么会有怎么大的变化能 就是因为对继承的不良使用 ,这也就是我们的动机。继承引入的静态特质,组合关系引入动态。
通过组合的关系进行支持未来的变化(多态);
GOF 中的装饰模式的定义:
动态(组合)的给一个对象增加一些额外的职责,就增加功能而言,Decorator模式比生成子类(继承)更为灵活,
(消除重复代码&减少子类个数)。
总结:
通过采用组合的而非继承的方式,Decorator模式实现了在运行是动态扩展的功能,而且根据需求扩展多个功能。
避免了使用继承带来的“灵活性”和“子类衍生”问题。
Decorator类设计接口上表现位is-a Componet 继承关系。但是实际上为has-a Componetd的组合关系。
在代码中一旦看到这样的关系 ,我们就可以按照Decorator模式来处理。
相关文章推荐
- 设计模式学习笔记(三)装饰模式(Decorator)
- 【设计模式学习笔记十】【结构型模式】【装饰模式(Decorator)】
- 设计模式学习笔记---装饰模式decorator(Java版)
- 设计模式学习笔记(2)之装饰模式(Decorator)
- 步步为营 .NET 设计模式学习笔记 十四、Decorator(装饰模式)
- 步步为营 .NET 设计模式学习笔记 十四、Decorator(装饰模式)
- 设计模式学习笔记十五:装饰模式(Decorator Pattern)
- 六 装饰模式(Decorator)——设计模式学习笔记
- java 设计模式学习笔记九 decorator装饰模式
- java 设计模式学习笔记九 decorator装饰模式
- 设计模式学习笔记--装饰(Decorator)模式
- 步步为营 .NET 设计模式学习笔记 十四、Decorator(装饰模式)
- 设计模式学习笔记十五:装饰模式(Decorator Pattern)
- 设计模式学习笔记(三)装饰模式(Decorator)
- 设计模式学习-Decorator(装饰)
- 设计模式学习系列七:装饰模式(Decorator)
- 设计模式学习笔记-装饰模式
- 设计模式学习笔记之装饰器模式
- 设计模式学习-Decorator(装饰)
- java设计模式学习笔记之装饰模式