设计模式 学习笔记 之 装饰模式 Decorator(6)

来源:互联网 发布:王者荣耀抽奖活动源码 编辑:程序博客网 时间:2024/05/16 07:43

之前的学习过程中 学习了单一职责类:

在软件组件设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求变化,子类极具膨胀,同时充斥着重复代码,

这时候关键就是划清责任。

今天就具体学习下单一职责中的装饰模式。

动机:在某些情况下我们可能会“过度的使用继承来拓展对象的功能”,由于继承为类型引入的静态特质,是的这种扩展

方式缺乏灵活性,并且随着子类的增多(扩展的功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。


如何使“对象功能的拓展”能够根据需要来动态的实现?同时便面“扩展功能的增多”带来的子类膨胀问题?

从而使得任何“功能扩展的变化”所导致的影响降低!


其实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模式来处理。






阅读全文
0 0
原创粉丝点击