天天看點

裝飾模式 Decorator

“單一職責”模式:

  • 在軟體元件的設計中,如果責任劃分的不清晰,使用繼承得到的結果往往是随着需求的變化,子類急劇膨脹,同時充斥着重複代碼,這時候的關鍵是劃清責任。
  • 典型模式
  1. Decorator
  2. Bridge

動機(Motivation)

  • 在某些情況下我們可能會“過度地使用繼承來擴充對象的功能”,由于繼承為類型引入的靜态特質,使得這種擴充方式缺乏靈活性;并且随着子類的增多(擴充功能的增多),各種子類的組合(擴充功能的組合)會導緻更多子類的膨脹。
  • 如何使“對象功能的擴充”能夠根據需要來動态地實作?同時避免“擴充功能的增多”帶來的子類膨脹問題?進而使得任何“功能擴充變化”所導緻的影響将為最低?

模式定義

動态(組合)地給一個對象增加一些額外的職責。就增加功能而言,Decorator模式比生成子類(繼承)更為靈活(消除重複代碼 & 減少子類個數)。 ——《設計模式》GoF

代碼示例

各種流操作。

繼承方式(僞代碼):

#include "iostream"

using namespace std;

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

//主體類
class FileStream : public Stream {
public:
    virtual char Read(int number) {
        //讀檔案流
        cout << "讀檔案流 " << number << endl;
    }
    virtual void Seek(int position) {
        //定位檔案流
        cout << "定位檔案流 " << position << endl;
    }
    virtual void Write(char data) {
        //寫檔案流
        cout << "寫檔案流 " << data << endl;
    }
};

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

class MemoryStream :public Stream {
public:
    virtual char Read(int number) {
        //讀記憶體流
        cout << "讀記憶體流 " << number << endl;
    }
    virtual void Seek(int position) {
        //定位記憶體流
        cout << "定位記憶體流 " << position << endl;
    }
    virtual void Write(char data) {
        //寫記憶體流
        cout << "寫記憶體流 " << data << endl;
    }

};

//擴充操作
class CryptoFileStream :public FileStream {
public:
    virtual char Read(int number) {

        //額外的加密操作...
        FileStream::Read(number);//讀檔案流

    }
    virtual void Seek(int position) {
        //額外的加密操作...
        FileStream::Seek(position);//定位檔案流
                                   //額外的加密操作...
    }
    virtual void Write(char data) {
        //額外的加密操作...
        FileStream::Write(data);//寫檔案流
                                //額外的加密操作...
    }
};

class CryptoNetworkStream:: public NetworkStream{
public:
    virtual char Read(int number) {
        //額外的加密操作...
        NetworkStream::Read(number);//讀網絡流
    }
    virtual void Seek(int position) {
        //額外的加密操作...
        NetworkStream::Seek(position);//定位網絡流
                                      //額外的加密操作...
    }
    virtual void Write(byte data) {
        //額外的加密操作...
        NetworkStream::Write(data);//寫網絡流
                                   //額外的加密操作...
    }
};

class CryptoMemoryStream : public MemoryStream {
public:
    virtual char Read(int number) {
        //額外的加密操作...
        MemoryStream::Read(number);//讀記憶體流
    }
    virtual void Seek(int position) {
        //額外的加密操作...
        MemoryStream::Seek(position);//定位記憶體流
                                     //額外的加密操作...
    }
    virtual void Write(byte data) {
        //額外的加密操作...
        MemoryStream::Write(data);//寫記憶體流
                                  //額外的加密操作...
    }
};

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();
}      
裝飾模式 Decorator

▲ 對繼承的不良使用,導緻太過于複雜。

組合方式實作多态:

由之前的靜态編譯時裝配改變成動态運作時裝配。

#include <iostream>

using namespace std;

//業務操作
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) {
        //讀檔案流
        cout << "讀檔案流 " << number << endl;
        return number;
    }
    virtual void Seek(int position) {
        //定位檔案流
        cout << "定位檔案流 " << position << endl;
    }
    virtual void Write(char data) {
        //寫檔案流
        cout << "寫檔案流 " << data << endl;
    }
};

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

class MemoryStream :public Stream {
public:
    virtual char Read(int number) {
        //讀記憶體流
        cout << "讀記憶體流 " << number << endl;
        return number;
    }
    virtual void Seek(int position) {
        //定位記憶體流
        cout << "定位記憶體流 " << position << endl;
    }
    virtual void Write(char data) {
        //寫記憶體流
        cout << "寫記憶體流 " << data << endl;
    }
};

//擴充操作
/*裝飾類:這裡有繼承,又有組合,大機率是裝飾模式*/
class  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) {
        //額外的加密操作...
        cout << "CryptoStream 額外的加密操作..." << endl;
        return stream->Read(number);//讀檔案流 /*這裡不再局限于檔案流,copy 注釋沒改*/
    }
    virtual void Seek(int position) {
        //額外的加密操作...
        cout << "CryptoStream 額外的加密操作..." << endl;
        stream->Seek(position);//定位檔案流
                               //額外的加密操作...
    }
    virtual void Write(/*byte*/char data) {
        //額外的加密操作...
        cout << "CryptoStream 額外的加密操作..." << endl;
        stream->Write(data);//寫檔案流
                            //額外的加密操作...
    }
};


class BufferedStream : public DecoratorStream {
public:
    BufferedStream(Stream* stm) :DecoratorStream(stm) {}
    //...
    virtual char Read(int number) {
        //額外的加密操作...
        cout << "BufferedStream 額外的加密操作..." << endl;
        return stream->Read(number);//讀...流
    }
    virtual void Seek(int position) {
        //額外的加密操作...
        cout << "BufferedStream 額外的加密操作..." << endl;
        stream->Seek(position);//定位...流
                               //額外的加密操作...
    }
    virtual void Write(/*byte*/char data) {
        //額外的加密操作...
        cout << "BufferedStream 額外的加密操作..." << endl;
        stream->Write(data);//寫...流
                            //額外的加密操作...
    }
};


void Process() {
    //運作時 裝配
    FileStream* s1 = new FileStream();
    CryptoStream* s2 = new CryptoStream(s1);

    s2->Read(101);
    s2->Seek(102);
    s2->Write('A');
    cout << endl;

    BufferedStream* s3 = new BufferedStream(s1);

    s3->Read(301);
    s3->Seek(302);
    s3->Write('B');
    cout << endl;

    BufferedStream* s4 = new BufferedStream(s2);

    s4->Read(401);
    s4->Seek(402);
    s4->Write('C');
    cout << endl;
}

int main()
{
    Process();

    getchar();
    return 0;
}      

/裝飾類:這裡有繼承,又有組合,大機率是裝飾模式/

代碼依賴關系:

裝飾模式 Decorator

輸出:

CryptoStream 額外的加密操作...
讀檔案流 101
CryptoStream 額外的加密操作...
定位檔案流 102
CryptoStream 額外的加密操作...
寫檔案流 A

BufferedStream 額外的加密操作...
讀檔案流 301
BufferedStream 額外的加密操作...
定位檔案流 302
BufferedStream 額外的加密操作...
寫檔案流 B

BufferedStream 額外的加密操作...
CryptoStream 額外的加密操作...
讀檔案流 401
BufferedStream 額外的加密操作...
CryptoStream 額外的加密操作...
定位檔案流 402
BufferedStream 額外的加密操作...
CryptoStream 額外的加密操作...
寫檔案流 C      

類圖

裝飾模式 Decorator

要點總結

  • 通過采用組合而非繼承的手法, Decorator模式實作了在運作時 動态擴充對象功能的能力,而且可以根據需要擴充多個功能。避免 了使用繼承帶來的“靈活性差”和“多子類衍生問題”。
  • Decorator類在接口上表現為is-a Component的繼承關系,即 Decorator類繼承了Component類所具有的接口。但在實作上又 表現為has-a Component的組合關系,即Decorator類又使用了 另外一個Component類。
  • Decorator模式的目的并非解決“多子類衍生的多繼承”問題, Decorator模式應用的要點在于解決“主體類在多個方向上的擴充 功能”——是為“裝飾”的含義。