c++ 設計模式6 (Decorator 裝飾模式)

來源:互聯網
上載者:User

標籤:查看   基礎上   new   char   代碼   物件導向   技術分享   參考文獻   本質   

4. “單一職責”類模式

在軟體組件的設計中,如果責任劃分的不清晰,使用繼承得到的結果往往是隨著需求的變化,子類急劇膨脹,同時充斥著重複代碼,這時候的關鍵是劃清責任。

典型模式代表: Decorator,Bridge

 

4.1 Decorator 裝飾模式

 

程式碼範例:不同的流操作(檔案流,網路流,記憶體流)及其擴充功能(加密,緩衝)等的實現

實現代碼1:

類圖結構示意(大量使用繼承)

資料規模: 假設有n種檔案,m種功能操作。該實現方法有(1 + n + n * m! / 2) 數量級的子類;

同時考察59行,79行,98行本身是相同的代碼(類似還有很多),存在大量的冗餘和重複。

開始重構,見方法2.

  1 //Decorator1.cpp  2 //業務操作  3 class Stream{  4 public:  5     virtual char Read(int number)=0;  6     virtual void Seek(int position)=0;  7     virtual void Write(char data)=0;  8       9     virtual ~Stream(){} 10 }; 11  12 //主體類 13 class FileStream: public Stream{ 14 public: 15     virtual char Read(int number){ 16         //讀檔案流 17     } 18     virtual void Seek(int position){ 19         //定位檔案流 20     } 21     virtual void Write(char data){ 22         //寫檔案流 23     } 24  25 }; 26  27 class NetworkStream :public Stream{ 28 public: 29     virtual char Read(int number){ 30         //讀網路流 31     } 32     virtual void Seek(int position){ 33         //定位網路流 34     } 35     virtual void Write(char data){ 36         //寫網路流 37     } 38      39 }; 40  41 class MemoryStream :public Stream{ 42 public: 43     virtual char Read(int number){ 44         //讀記憶體流 45     } 46     virtual void Seek(int position){ 47         //定位記憶體流 48     } 49     virtual void Write(char data){ 50         //寫記憶體流 51     } 52      53 }; 54  55 //擴充操作 56 class CryptoFileStream :public FileStream{ 57 public: 58     virtual char Read(int number){ 59         60         //額外的加密操作... 61         FileStream::Read(number);//讀檔案流 62          63     } 64     virtual void Seek(int position){ 65         //額外的加密操作... 66         FileStream::Seek(position);//定位檔案流 67         //額外的加密操作... 68     } 69     virtual void Write(byte data){ 70         //額外的加密操作... 71         FileStream::Write(data);//寫檔案流 72         //額外的加密操作... 73     } 74 }; 75  76 class CryptoNetworkStream : :public NetworkStream{ 77 public: 78     virtual char Read(int number){ 79          80         //額外的加密操作... 81         NetworkStream::Read(number);//讀網路流 82     } 83     virtual void Seek(int position){ 84         //額外的加密操作... 85         NetworkStream::Seek(position);//定位網路流 86         //額外的加密操作... 87     } 88     virtual void Write(byte data){ 89         //額外的加密操作... 90         NetworkStream::Write(data);//寫網路流 91         //額外的加密操作... 92     } 93 }; 94  95 class CryptoMemoryStream : public MemoryStream{ 96 public: 97     virtual char Read(int number){ 98          99         //額外的加密操作...100         MemoryStream::Read(number);//讀記憶體流101     }102     virtual void Seek(int position){103         //額外的加密操作...104         MemoryStream::Seek(position);//定位記憶體流105         //額外的加密操作...106     }107     virtual void Write(byte data){108         //額外的加密操作...109         MemoryStream::Write(data);//寫記憶體流110         //額外的加密操作...111     }112 };113 114 class BufferedFileStream : public FileStream{115     //...116 };117 118 class BufferedNetworkStream : public NetworkStream{119     //...120 };121 122 class BufferedMemoryStream : public MemoryStream{123     //...124 }125 126 127 128 129 class CryptoBufferedFileStream :public FileStream{130 public:131     virtual char Read(int number){132         133         //額外的加密操作...134         //額外的緩衝操作...135         FileStream::Read(number);//讀檔案流136     }137     virtual void Seek(int position){138         //額外的加密操作...139         //額外的緩衝操作...140         FileStream::Seek(position);//定位檔案流141         //額外的加密操作...142         //額外的緩衝操作...143     }144     virtual void Write(byte data){145         //額外的加密操作...146         //額外的緩衝操作...147         FileStream::Write(data);//寫檔案流148         //額外的加密操作...149         //額外的緩衝操作...150     }151 };152 153 154 155 void Process(){156 157         //編譯時間裝配158     CryptoFileStream *fs1 = new CryptoFileStream();159 160     BufferedFileStream *fs2 = new BufferedFileStream();161 162     CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();163 164 }

 

實現代碼2:

針對上述代碼,重構步驟如下:

1)考察 CryptoFileStream ,CryptoNetworkStream,CryptoMemoryStream三個類,將其繼承FileStream,NetworkStream,NetworkStream改為組合;即

 1 class CryptoFileStream{ 2     FileStream* stream; 3 public: 4     virtual char Read(int number){ 5         6         //額外的加密操作... 7         stream -> Read(number);//改用欄位方式調用Read() 8         // ...seek() write() 同理 9     }10 }    11 12 class CryptoNetworkStream{13     NetworkStream* stream;14 public:15     virtual char Read(int number){16         17         //額外的加密操作...18         stream -> Read(number);//改用欄位方式調用Read()19         //... seek() write() 同理20     }21 }    22 23 class CryptoMemoryStream{24     MemoryStream* stream;25 public:26     virtual char Read(int number){27         28         //額外的加密操作...29         stream -> Read(number);//改用欄位方式調用Read()30         //... seek() write() 同理31     }32 }    

2)考察上述2行, 13行, 24行, 發現其均為Stream子類, 應使用多態性繼續重構。

 1 class CryptoFileStream{ 2     Stream* stream; // = new FileStream() 3 public: 4     virtual char Read(int number){ 5         6         //額外的加密操作... 7         stream -> Read(number);//改用欄位方式調用Read() 8         // ...seek() write() 同理 9     }10 }    11 12 class CryptoNetworkStream{13     Stream* stream; // = new NetworkStream();14 public:15     virtual char Read(int number){16         17         //額外的加密操作...18         stream -> Read(number);//改用欄位方式調用Read()19         //... seek() write() 同理20     }21 }    22 23 class CryptoMemoryStream{24     Stream* stream; // = newMemoryStream()25 public:26     virtual char Read(int number){27         28         //額外的加密操作...29         stream -> Read(number);//改用欄位方式調用Read()30         //... seek() write() 同理31     }32 }    

3)發現三個類是相同的,不同的實現(需求的變化)是在運行時實現,編譯時間複用,改為一個類即可,命名為CryptoStream。

同時為了保證介面規範(read,seek等仍然是虛函數),繼承Stream,出現既有組合,又有繼承的情況。

 1 class CryptoStream : public Stream{ 2     Stream* stream; // = new ... 3 public: 4     virtual char Read(int number){ 5         6         //額外的加密操作... 7         stream -> Read(number);//改用欄位方式調用Read() 8         // ...seek() write() 同理 9     }10 }   

4)添加相應構造器,得到此輪重構後的結果,代碼如下,主要查看使用方式(運行時裝配):

  1 //Decorator2.cpp  2 class Stream{  3   4 public:  5     virtual char Read(int number)=0;  6     virtual void Seek(int position)=0;  7     virtual void Write(char data)=0;  8       9     virtual ~Stream(){} 10 }; 11  12 //主體類 13 class FileStream: public Stream{ 14 public: 15     virtual char Read(int number){ 16         //讀檔案流 17     } 18     virtual void Seek(int position){ 19         //定位檔案流 20     } 21     virtual void Write(char data){ 22         //寫檔案流 23     } 24  25 }; 26  27 class NetworkStream :public Stream{ 28 public: 29     virtual char Read(int number){ 30         //讀網路流 31     } 32     virtual void Seek(int position){ 33         //定位網路流 34     } 35     virtual void Write(char data){ 36         //寫網路流 37     } 38      39 }; 40  41 class MemoryStream :public Stream{ 42 public: 43     virtual char Read(int number){ 44         //讀記憶體流 45     } 46     virtual void Seek(int position){ 47         //定位記憶體流 48     } 49     virtual void Write(char data){ 50         //寫記憶體流 51     } 52      53 }; 54  55 //擴充操作 56  57  58 class CryptoStream: public Stream { 59      60     Stream* stream;//... 61  62 public: 63     CryptoStream(Stream* stm):stream(stm){ 64      65     } 66      67      68     virtual char Read(int number){ 69         70         //額外的加密操作... 71         stream->Read(number);//讀檔案流 72     } 73     virtual void Seek(int position){ 74         //額外的加密操作... 75         stream::Seek(position);//定位檔案流 76         //額外的加密操作... 77     } 78     virtual void Write(byte data){ 79         //額外的加密操作... 80         stream::Write(data);//寫檔案流 81         //額外的加密操作... 82     } 83 }; 84  85  86  87 class BufferedStream : public Stream{ 88      89     Stream* stream;//... 90      91 public: 92     BufferedStream(Stream* stm):stream(stm){ 93          94     } 95     //... 96 }; 97  98  99 100 101 102 void Process(){103 104     //運行時裝配105     FileStream* s1=new FileStream();106     CryptoStream* s2=new CryptoStream(s1);107     108     BufferedStream* s3=new BufferedStream(s1);109     110     BufferedStream* s4=new BufferedStream(s2);111     112     113 114 }

 

實現代碼3:

上述實現代碼2已經極大地緩解了冗餘問題,符合物件導向的設計思想,該輪重構是錦上添花。

重構步驟如下:

考察上述代碼,多個子類都有同樣的欄位(Stream* stream;//...)

應考慮“往上提”,方法有兩種,第一種是提到基類(顯然不合適,FileStream等並不需要Stream欄位 )

所以考慮第二種方法,實現一個“中間類”。

DecoratorStream: public Stream{protected:    Stream* stream;//...        DecoratorStream(Stream * stm):stream(stm){        }    };

CryptoStream等繼承中間類DecoratorStream:

class CryptoStream: public DecoratorStream { public:    CryptoStream(Stream* stm):DecoratorStream(stm){        }    //...}    

重構完成的最終版本:

FileStream,NetworkStream,MemoryStream等可以建立各自的對象;

但實現加密,緩衝功能必須在已有FileStream/NetworkStream等對象基礎上;

這些操作本質是擴充操作,也就是“裝飾”的含義。

此時類圖示意:

這時類的數量為(1 + n + 1 + m)

 

 

 

 

 

  1 //Decorator3.cpp  2 class Stream{  3   4 public:  5     virtual char Read(int number)=0;  6     virtual void Seek(int position)=0;  7     virtual void Write(char data)=0;  8       9     virtual ~Stream(){} 10 }; 11  12 //主體類 13 class FileStream: public Stream{ 14 public: 15     virtual char Read(int number){ 16         //讀檔案流 17     } 18     virtual void Seek(int position){ 19         //定位檔案流 20     } 21     virtual void Write(char data){ 22         //寫檔案流 23     } 24  25 }; 26  27 class NetworkStream :public Stream{ 28 public: 29     virtual char Read(int number){ 30         //讀網路流 31     } 32     virtual void Seek(int position){ 33         //定位網路流 34     } 35     virtual void Write(char data){ 36         //寫網路流 37     } 38      39 }; 40  41 class MemoryStream :public Stream{ 42 public: 43     virtual char Read(int number){ 44         //讀記憶體流 45     } 46     virtual void Seek(int position){ 47         //定位記憶體流 48     } 49     virtual void Write(char data){ 50         //寫記憶體流 51     } 52      53 }; 54  55 //擴充操作 56  57 DecoratorStream: public Stream{ 58 protected: 59     Stream* stream;//... 60      61     DecoratorStream(Stream * stm):stream(stm){ 62      63     } 64      65 }; 66  67 class CryptoStream: public DecoratorStream { 68   69  70 public: 71     CryptoStream(Stream* stm):DecoratorStream(stm){ 72      73     } 74      75      76     virtual char Read(int number){ 77         78         //額外的加密操作... 79         stream->Read(number);//讀檔案流 80     } 81     virtual void Seek(int position){ 82         //額外的加密操作... 83         stream::Seek(position);//定位檔案流 84         //額外的加密操作... 85     } 86     virtual void Write(byte data){ 87         //額外的加密操作... 88         stream::Write(data);//寫檔案流 89         //額外的加密操作... 90     } 91 }; 92  93  94  95 class BufferedStream : public DecoratorStream{ 96      97     Stream* stream;//... 98      99 public:100     BufferedStream(Stream* stm):DecoratorStream(stm){101         102     }103     //...104 };105 106 107 108 109 void Process(){110 111     //運行時裝配112     FileStream* s1=new FileStream();113     114     CryptoStream* s2=new CryptoStream(s1);115     116     BufferedStream* s3=new BufferedStream(s1);117     118     BufferedStream* s4=new BufferedStream(s2);119     120     121 122 }

 

Decorator模式使用動機:

在某些情況下我們可能會“過度地使用繼承來擴充項物件的功能”,由於基礎為類型引入的靜態特指,使得這種擴充方式缺乏靈活性;並且隨著子類的增多(擴充功能的增多),各個子類的組合(擴充功能的組合)會導致各種子類的膨脹。

 

模式定義:

動態(組合)地給一個對象增加一些額外的指責。就增加功能而言,Decorator模式比聲場子類(繼承)更為靈活(消除重複代碼&減少子類個數)

 

類圖:

 

要點總結:

1.通過採用組合并非繼承的手法,Decorator模式實現了在運行時動態擴充項物件功能的能力,而且可以根據需要擴充多個功能。避免了使用繼承帶來的”靈活性差“和”多子類衍生問題“

2.Decorator類在介面上表現為is-a Component的繼承關係,即Decorator類繼承了Component類所具有的介面。但在實現上又表現為has-a Component的組合關係,即Decorator類又使用了另外一個Component類。

3.Decorator模式的目的並非解決”多字類衍生的多繼承“問題,Decorator模式應用的要點在於解決”主體類在多個方向上的擴充功能“(顯然file,network與加密,緩衝是兩種擴充方向) ——是為”裝飾“的含義。

 

參考文獻:

李建忠老師 《C++設計模式》網路課程

《設計模式:可複用物件導向軟體的基礎》

c++ 設計模式6 (Decorator 裝飾模式)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.