6 設計模式_裝飾模式(Decorator)

Decorator 裝飾模式

“單一職責”模式:
  • 在軟件組件的設計中,如果責任劃分的不清晰,使用繼承得到的結果往往是隨着需求的變化,子類急劇膨脹,同時充斥着重複代碼,這時候的關鍵是劃清責任
  • 典型模式
    • Decorator
    • Bridge
動機 (Motivation)
  • 在某些情況下我們可能會“過度地使用繼承來擴展對象的功能”,由於繼承爲類型引入的靜態特質,使得這種擴展方式缺乏靈活性;並且隨着子類的增多(擴展功能的增多),各種子類的組合(擴展功能的組合)會導致更多子類的膨脹。
  • 如何使“對象功能的擴展”能夠根據需要來動態地實現?同時避免“擴展功能的增多”帶來的子類膨脹問題?從而使得任何“功能擴展變化”所導致的影響將爲最低?
舉個例子
  • 使用模式前
//業務操作,數據流的讀寫定位操作
class Stream{
public:
    virtual char Read(int number)=0; 
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;
    
    virtual ~Stream(){}
};

//文件流
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //讀文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //寫文件流
    }

};
//網絡數據流
class NetworkStream :public Stream{
public:
	//省略
};

//內存數據流
class MemoryStream :public Stream{
public:    
	//省略
};

//對文件流的加密操作
class CryptoFileStream :public FileStream{
public:
    virtual char Read(int number){
       
        //額外的加密操作...
        FileStream::Read(number);//讀文件流
        
    }
    virtual void Seek(int position){
        //額外的加密操作...
        FileStream::Seek(position);//定位文件流
        //額外的加密操作...
    }
    virtual void Write(byte data){
        //額外的加密操作...
        FileStream::Write(data);//寫文件流
        //額外的加密操作...
    }
};
//對網絡流的加密
class CryptoNetworkStream : :public NetworkStream{
public:
	//省略
};
// 對內存流的加密
class CryptoMemoryStream : public MemoryStream{
public:
	//省略
};
//文件流緩存
class BufferedFileStream : public FileStream{
    //...
};
//網絡流緩存
class BufferedNetworkStream : public NetworkStream{
    //...
};
//內存流緩存
class BufferedMemoryStream : public MemoryStream{
    //...
}

//對文件流既加密又緩存
class CryptoBufferedFileStream :public FileStream{
public:
    virtual char Read(int number){
        
        //額外的加密操作...
        //額外的緩衝操作...
        FileStream::Read(number);//讀文件流
    }
    virtual void Seek(int position){
        //額外的加密操作...
        //額外的緩衝操作...
        FileStream::Seek(position);//定位文件流
        //額外的加密操作...
        //額外的緩衝操作...
    }
    virtual void Write(byte data){
        //額外的加密操作...
        //額外的緩衝操作...
        FileStream::Write(data);//寫文件流
        //額外的加密操作...
        //額外的緩衝操作...
    }
};
//對網絡流既加密又緩衝
//對內存流既加密又緩衝

void Process(){
        //編譯時裝配
    CryptoFileStream *fs1 = new CryptoFileStream();
    BufferedFileStream *fs2 = new BufferedFileStream();
    CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();
}

可以看到,因爲主體類在多個方向的擴展(數據流的讀寫定位,以及加密緩存是爲多個方向),當對數據流(文件流,網絡流,內存流)進行操作(加密,緩存)時,此時會派生出大量的類。而在這個過程中的操作有相同。

  • 使用模式後
//業務操作
class Stream{
public:
    virtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;
    virtual ~Stream(){}
};

//主體類
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //讀文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //寫文件流
    }
};

class NetworkStream :public Stream{
public:
    virtual char Read(int number){
        //讀網絡流
    }
    virtual void Seek(int position){
        //定位網絡流
    }
    virtual void Write(char data){
        //寫網絡流
    }  
};

class MemoryStream :public Stream{
public:
    virtual char Read(int number){
        //讀內存流
    }
    virtual void Seek(int position){
        //定位內存流
    }
    virtual void Write(char data){
        //寫內存流
    }
};

  • is a Stream 並且 has a Stream
  • is a Stream是爲了接口的統一性。
  • has a Stream 是爲了充分利用多態性,來消除沒必要的派生類。
    既繼承又組合是裝飾模式強大,巧妙的地方。

//裝飾器
DecoratorStream: public Stream{
protected:
    Stream* stream;//抽象類(可以是文件類)
    //構造函數時,stream需要賦值。
  	// (可能是,文件流,內存流,網絡流,加密文件流,加密內存流,加密網絡流,緩存內存流,緩存文件流,緩存網絡流)
    DecoratorStream(Stream * stm):stream(stm){
    }
};
//擴展操作
class CryptoStream: public DecoratorStream {
public:
    CryptoStream(Stream* stm):DecoratorStream(stm){ 
    }        
    virtual char Read(int number){     
        //額外的加密操作...
        stream->Read(number);//多態指針stream讀文件流
    }
    virtual void Seek(int position){
        //額外的加密操作...
        stream::Seek(position);//定位文件流
        //額外的加密操作...
    }
    virtual void Write(byte data){
        //額外的加密操作...
        stream::Write(data);//寫文件流
        //額外的加密操作...
    }
};

class BufferedStream : public DecoratorStream{
public:
    BufferedStream(Stream* stm):DecoratorStream(stm){
    }
     virtual char Read(int number){     
        //額外的緩存操作...
        stream->Read(number);//多態指針stream讀文件流
    }
    virtual void Seek(int position){
        //額外的緩存操作.
         stream->Seek(position);//定位流
    }
    virtual void Write(byte data){
	/ /額外的緩存操作.
	      stream->Write(data);//寫流   
    }
};
void Process(){

    //運行時裝配
    FileStream* s1=new FileStream();
    
    CryptoStream* s2=new CryptoStream(s1);//對文件流加密
    
    BufferedStream* s3=new BufferedStream(s1);//緩存文件流
    
    BufferedStream* s4=new BufferedStream(s2);//緩存(加密文件流)
}
結構(Structure)
  • 紅色框內是穩定的部分
    • Comonent 就是例子中的Stream類,有讀寫定位行爲。

    • Decorator裝飾器繼承Component,並且組合了一個Component指針.

  • 藍色框內是變化的部分
    • ConcreteComponent是其子類。(FileStream,NetworkStrem,BufferStream是Stream的子類)

    • ConcreteDecoratorA,(對流加密操作類)(CryptoStream)

    • ConcreteDecoratorB, ( 對流緩存操作類) (BufferedStream)

  • 在Component進行對應的Operation時(裝飾器也是Component),在子類進行相應的Operation後,因爲裝飾器中還有一個Component指針,還可以對其進行相應的操作也就是component->Operation();從而達到了裝飾作用。就是對裝飾器中的Component指針進行裝飾。

在這裏插入圖片描述

要點總結
  • 通過採用組合而非繼承的手法, Decorator模式實現了在運行時動態擴展對象功能的能力,而且可以根據需要擴展多個功能。避免了使用繼承帶來的“靈活性差”和“多子類衍生問題
  • Decorator類在接口上表現爲is-a Component的繼承關係,即Decorator類繼承了Component類所具有的接口。但在實現上又表現爲has-a Component的組合關係,即Decorator類又使用了另外一個Component類
  • Decorator模式的目的並非解決“多子類衍生的多繼承”問題,Decorator模式應用的要點在於解決“主體類在多個方向上的擴展功能”——是爲“裝飾”的含義。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章