單一職責模式:在軟件組件的設計中,若是責任劃分的不清晰,使用繼承獲得的結果每每是隨着需求的變化,子類急劇膨脹,同時充斥着重複代碼,這時候的關鍵是劃清責任。緩存
典型模式:裝飾模式(Decorator)、橋接模式(Bridge)。網絡
在某些狀況下咱們可能會「過分地使用繼承來擴展對象的功能」,因爲繼承爲類型引入的靜態特質,使得這種擴展方式缺少靈活性;而且隨着子類的增多(擴展功能的增多),各類子類的組合(擴展功能的組合)會致使更多子類的膨脹。加密
使「對象功能的擴展」可以根據須要來動態地實現;同時避免「擴展功能的增多」帶來的子類膨脹問題,使得任何「功能擴展變化」所致使的影響降爲最低。spa
動態(組合)地給一個對象增長一些額外的職責。就增長功能而言,Decorator模式比生成子類(繼承)更爲靈活(消除重複代碼、減小子類個數)。設計
//原有代碼
//業務操做
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: //... }; class MemoryStream :public Stream{ public: //... }; //擴展操做 DecoratorStream: public Stream{ protected: 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);//讀文件流 } virtual void Seek(int position){ //額外的加密操做... stream::Seek(position);//定位文件流 } virtual void Write(byte data){ //額外的加密操做... stream::Write(data);//寫文件流 } }; class BufferedStream : public DecoratorStream{ Stream* stream;//... public: BufferedStream(Stream* stm):DecoratorStream(stm){ } //... }; void Process(){ //運行時裝配 FileStream* s1=new FileStream(); CryptoStream* s2=new CryptoStream(s1); BufferedStream* s3=new BufferedStream(s1); CryptoBufferedFileStream* s4=new BufferedStream(s2); }
5.解析3d
這是一個內容流程序,有讀、定位(搜索)和寫等功能,在業務上有文件流、網絡流和內存流等,擴展出的功能包括額外加密操做和緩存操做等。指針
在原有代碼中,以繼承的方法擴展這些功能,很詳細也很好理解,但存在如下問題:繼承引入了靜態特質,使得這種擴展方式缺少靈活性;而且隨着子類的增多(擴展功能的增多),各類子類的組合會致使更多子類的膨脹,以下圖所示。假設一級功能有n種,二級功能有m種,則該程序一共有(1+n+X)個類。(思考X用n和m怎麼表示)。code
在運用裝飾模式後的代碼中,經過動態(組合)地給一個對象增長一些額外的職責,其結構如圖所示,則該程序一共有(1+n+1+m)個類,相比以前大大減少了類的個數,且經過動態組合,將編譯時依賴轉化爲運行時依賴。對象
其中,Component(抽象類,如Stream):blog
1.定義一個對象接口,能夠給這些對象動態地添加職責。
ConcreteComponent(具體類,如FileStream、NetworkStream)
1.定義一個對象,能夠給這個對象添加一些職責
Decorator(裝飾抽象類,如DecoratorStream)
1.維持一個指向Component對象的指針,並定義一個與Component接口一致的接口。
ConcreteDecorator(具體裝飾類,如CryptoStream、BufferedStream)
1.向組件添加職責
1.經過採用組合而非繼承的手法,Decorator模式實現了在運行時動態擴展對象功能的能力,並且能夠根據須要擴展多個功能。避免了使用繼承帶來的「靈活性差」和「多子類衍生問題」。
2.Decorator類在接口上表現爲is-a Component的繼承關係,即Decorator類繼承了Component類所具備的接口。但在實現上又表現爲has-a Component的組合關係,即Decorator類又使用了另一個Component類。
3.Decorator模式的目的並不是解決「多子類衍生的多繼承」問題,Decorator模式應用的要點在於解決「主體類在多個方向上的擴展功能」——是爲「裝飾」的含義。
因爲某些類型固有的實現邏輯,使得它們具備兩個變化的維度,乃至多個緯度的變化。
使得該類型能夠輕鬆地沿着兩個乃至多個方向變化,而不引入額外的複雜度,以應對這種「多維度的變化」。
將抽象部分(業務功能)與實現部分(平臺實現)分離,使它們均可以獨立地變化。
//原有代碼
class Messager{
public: virtual void Login(string username, string password)=0; virtual void SendMessage(string message)=0; virtual void SendPicture(Image image)=0; virtual void PlaySound()=0; virtual void DrawShape()=0; virtual void WriteText()=0; virtual void Connect()=0; virtual ~Messager(){} }; //平臺實現 class PCMessagerBase : public Messager{ public: virtual void PlaySound(){ //********** } virtual void DrawShape(){ //********** } virtual void WriteText(){ //********** } virtual void Connect(){ //********** } }; class MobileMessagerBase : public Messager{ public: //********** }; //業務抽象 class PCMessagerLite : public PCMessagerBase { public: virtual void Login(string username, string password){ PCMessagerBase::Connect(); } virtual void SendMessage(string message){ PCMessagerBase::WriteText(); } virtual void SendPicture(Image image){ PCMessagerBase::DrawShape(); } }; class PCMessagerPerfect : public PCMessagerBase { public: virtual void Login(string username, string password){ PCMessagerBase::PlaySound(); PCMessagerBase::Connect(); } virtual void SendMessage(string message){ PCMessagerBase::PlaySound(); PCMessagerBase::WriteText(); } virtual void SendPicture(Image image){ PCMessagerBase::PlaySound(); PCMessagerBase::DrawShape(); } }; class MobileMessagerLite : public MobileMessagerBase { public: //********** }; class MobileMessagerPerfect : public MobileMessagerBase { public: //********** }; void Process(){ //編譯時裝配 Messager *m = new MobileMessagerPerfect(); }
//運用橋接模式後代碼
class Messager{
protected: MessagerImp* messagerImp;//... public: virtual void Login(string username, string password)=0; virtual void SendMessage(string message)=0; virtual void SendPicture(Image image)=0; virtual ~Messager(){} }; class MessagerImp{ public: virtual void PlaySound()=0; virtual void DrawShape()=0; virtual void WriteText()=0; virtual void Connect()=0; virtual MessagerImp(){} }; //平臺實現 n class PCMessagerImp : public MessagerImp{ public: virtual void PlaySound(){ //********** } virtual void DrawShape(){ //********** } virtual void WriteText(){ //********** } virtual void Connect(){ //********** } }; class MobileMessagerImp : public MessagerImp{ public: //********** }; //業務抽象 m //類的數目:1+n+m class MessagerLite :public Messager { public: virtual void Login(string username, string password){ messagerImp->Connect(); } virtual void SendMessage(string message){ messagerImp->WriteText(); } virtual void SendPicture(Image image){ messagerImp->DrawShape(); } }; class MessagerPerfect :public Messager { public: virtual void Login(string username, string password){ messagerImp->PlaySound(); messagerImp->Connect(); } virtual void SendMessage(string message){ messagerImp->PlaySound(); messagerImp->WriteText(); } virtual void SendPicture(Image image){ messagerImp->PlaySound(); messagerImp->DrawShape(); } }; void Process(){ //運行時裝配 MessagerImp* mImp=new PCMessagerImp(); Messager *m =new Messager(mImp); }
5.解析
這是一個關於在不一樣平臺(PC和Moblie)上,實現不一樣業務(MessagerLite、MessagerPerfect)的程序。
原有的代碼與裝飾模式中原有代碼相似,經過不斷地繼承以擴展功能,但缺點也是顯而易見的,容易形成子類的龐大不靈活。
在運用橋接模式的代碼中,將抽象部分(業務功能)與實現部分(平臺實現)分離,使得抽象和實現能夠沿着各自的維度來變化。
其中,
1.Abstraction:定義抽象類的接口;維護一個指向Implementtor類型對象的指針;
2.RefinedAbstraction:擴充由Abstraction定義的接口;
3.Implementor:定義實現類的接口,該接口不必定要與Abstraction的接口徹底一致,甚至能夠徹底不一樣;
4.ConcreteImplementor:實現Implementor接口並定義它的具體實現。
1.Bridge模式使用「對象間的組合關係」解耦了抽象和實現之間固有的綁定關係,使得抽象和實現能夠沿着各自的維度來變化。所謂抽象和實現沿着各自緯度的變化,即「子類化」它們。
2.Bridge模式有時候相似於多繼承方案,可是多繼承方案每每違背單一職責原則(即一個類只有一個變化的緣由),複用性比較差。 Bridge模式是比多繼承方案更好的解決方法。
3.Bridge模式的應用通常在「兩個很是強的變化維度」,有時一個類也有多於兩個的變化維度,這時可使用Bridge的擴展模式。