設計模式--Note3--單一職責類
阿新 • • 發佈:2021-09-08
Decorator
裝飾
動態(組合)地給一個物件增加一些額外的職責。就增加功能而言,Decorator模式比生成子類(繼承)更為靈活(消除重複程式碼&減少子類個數)。
解決什麼問題
在某些情況下我們可能會”過度地使用繼承來擴充套件物件的功能“,由於繼承為型別引入的靜態特質(繼承自基類的東西無法改變,被綁死在這個基類上),使得這種擴充套件方式缺乏靈活性;並且隨著子類的增多(擴充套件功能的增多),各種子類的組合(擴充套件功能的組合)會導致更多子類的膨脹。
如何使”物件功能的擴充套件“能夠根據需要來動態地實現?同時避免”擴充套件功能的增多“帶來的子類膨脹問題?從而使得任何”功能擴充套件變化“所導致的影響降為最低。
結構
要點總結
- 通過採用組合而非繼承的手法,Decorator模式實現了在運動時動態擴充套件物件功能的能力,而且可以根據需要擴充套件多個功能。避免了使用繼承帶來的”靈活性差“和”多子類衍生問題“。
- Decorator類在介面上表現為is-a Component的繼承關係,即Decorator類繼承了Component類所具有的介面。但在實現上又表現為has-a Component的組合關係,即Decorator類又使用了另外一個Component類。
- Decorator模式的目的並非解決”多子類衍生的多繼承“問題,Decorator模式應用的要點在於解決”主體類在多個方向上的擴充套件功能“--是為”裝飾“的含義。
注意:當某個類繼承自另一個類,且擁有該類指標,則很有可能採用的是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: 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) { // 記憶體流寫 } }; // 擴充套件操作 // 加密功能 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(char data) { // 額外的加密操作 NetworkStream::Write(data); // 讀檔案流 // ... } }; class CryptoMemoryStream : public MemoryStream { // ... }; // 緩衝功能 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); } // ... }; // ... 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) {
// 記憶體流寫
}
};
// 擴充套件操作
// 加密功能
class CryptoStream : public Stream {
Stream* stream; // 將由繼承而被固定的 改為執行時動態的繫結
public:
CryptoStream(Stream* stm) : stream(stm) { }
virtual char Read(int number) {
// 額外的加密操作
stream->Read(number); // 讀檔案流
// ...
}
virtual void Seek(int position) {
// 額外的加密操作
stream->Seek(position); // 讀檔案流
// ...
}
virtual void Write(char data) {
// 額外的加密操作
stream->Write(data); // 讀檔案流
// ...
}
};
// 緩衝功能
class BufferedStream : public Stream {
// ...
};
void Process() {
// 執行時裝配
FileStream *s1 = new FileStream();
CryptoStream* s2 = new CryptoStream(s1);
BufferedStream *s3 = new BufferedStream(s1);
BufferedStream* s4 = new BufferedStream(s2);
}
// 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:
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) {
// 記憶體流寫
}
};
// 擴充套件操作
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) {
// 額外的加密操作
stream->Read(number); // 讀檔案流
// ...
}
virtual void Seek(int position) {
// 額外的加密操作
stream->Seek(position); // 讀檔案流
// ...
}
virtual void Write(char data) {
// 額外的加密操作
stream->Write(data); // 讀檔案流
// ...
}
};
// 緩衝功能
class BufferedStream : public DecoratorStream {
// ...
};
void Process() {
// 執行時裝配
FileStream *s1 = new FileStream();
CryptoStream* s2 = new CryptoStream(s1);
BufferedStream *s3 = new BufferedStream(s1);
BufferedStream* s4 = new BufferedStream(s2);
}
此時的類圖關係
Bridge
橋
將抽象部分(業務功能)與實現部分(平臺實現)分離,使得它們都可以獨立地變化。
解決什麼問題
由於某些型別的固有的實現邏輯,使得它們具有兩個變化的維度,乃至多個變化的維度。
如何應對這種”多維度的變化“?如何利用面向物件技術來使得型別可以輕鬆地沿著兩個乃至多個方向變化,而不引入額外的複雜度?
結構
要點總結
- Bridge模式使用”物件間的組合關係“解耦了抽象和實現之間固有的繫結關係,使得抽象和實現可以沿著各自的維度來變化。所謂抽象和實現沿著各自的維度的變化,即”子類化“它們。
- Bridge模式有時候勒斯與多繼承方案,但是多繼承方案往往違背單一職責原則(即一個類只有一個變化的原因),複用性比較差。Bridge模式是比多繼承方案更好的解決辦法。
- Bridge模式的應用一般在”兩個非常強的變化維度“,有時一個類也有多於兩個的變化維度,這時可以使用Bridge的擴充套件模式。
示例:Message輕量版和完美版,且對應不同平臺
// 最初實現版本
class Messager {
public:
virtual ~Messager() {}
virtual void Login(const std::string& username, const std::string& password) = 0;
virtual void SendMessage(const std::string& message) = 0;
virtual void SendPicture(const Image& picture) = 0;
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;
};
// 平臺實現
class PCMessagerBase : public Message {
public:
virtual void PlaySound() {
// ...
}
virtual void DrawShape() {
// ...
}
virtual void WriteText() {
// ...
}
virtual void Connect() {
// ...
}
}
class MobileMessagerBase : public Message {
public:
virtual void PlaySound() {
// ...
}
virtual void DrawShape() {
// ...
}
virtual void WriteText() {
// ...
}
virtual void Connect() {
// ...
}
}
// 業務抽象
class PCMessagerLite : public PCMessagerBase {
public:
virtual void Login(const std::string& username, const std::string& password) {
PCMessagerBase::Connect();
// ...
}
virtual void SendMessage(const std::string& message) {
PCMessagerBase::WriteText();
// ...
}
virtual void SendPicture(const Image& picture) {
PCMessagerBase::DrawShape();
// ...
}
};
class PCMessagerPerfect : public PCMessagerBase {
public:
virtual void Login(const std::string& username, const std::string& password) {
PCMessagerBase::PlaySound();
// ...
PCMessagerBase::Connect();
// ...
}
virtual void SendMessage(const std::string& message) {
PCMessagerBase::PlaySound();
// ...
PCMessagerBase::WriteText();
// ...
}
virtual void SendPicture(const Image& picture) {
PCMessagerBase::PlaySound();
// ...
PCMessagerBase::DrawShape();
// ...
}
};
class MobileMessagerLite : public MobileMessagerBase {
public:
virtual void Login(const std::string& username, const std::string& password) {
MobileMessagerBase::Connect();
// ...
}
virtual void SendMessage(const std::string& message) {
MobileMessagerBase::WriteText();
// ...
}
virtual void SendPicture(const Image& picture) {
MobileMessagerBase::DrawShape();
// ...
}
};
class MobileMessagerPerfect : public MobileMessagerBase {
public:
virtual void Login(const std::string& username, const std::string& password) {
MobileMessagerBase::PlaySound();
// ...
MobileMessagerBase::Connect();
// ...
}
virtual void SendMessage(const std::string& message) {
MobileMessagerBase::PlaySound();
// ...
MobileMessagerBase::WriteText();
// ...
}
virtual void SendPicture(const Image& picture) {
MobileMessagerBase::PlaySound();
// ...
MobileMessagerBase::DrawShape();
// ...
}
};
// ...
void Process() {
// 編譯時裝配
Message* m = new MobileMessagerPerfect();
}
// Bridge
class Messager {
protected:
MessagerImp* messagerImp;
public:
Messager(MessagerImp* imp) : messagerImp(imp) {
}
virtual ~Messager() {}
virtual void Login(const std::string& username, const std::string& password) = 0;
virtual void SendMessage(const std::string& message) = 0;
virtual void SendPicture(const Image& picture) = 0;
};
class MessagerImp {
public:
virtual ~MessagerImp() {}
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;
};
// 平臺實現
class PCMessagerImp : public MessagerImp {
public:
virtual void PlaySound() {
// ...
}
virtual void DrawShape() {
// ...
}
virtual void WriteText() {
// ...
}
virtual void Connect() {
// ...
}
}
class MobileMessagerImp : public MessagerImp {
public:
virtual void PlaySound() {
// ...
}
virtual void DrawShape() {
// ...
}
virtual void WriteText() {
// ...
}
virtual void Connect() {
// ...
}
}
// 業務抽象
class MessagerLite : public Messager {
public:
MessagerLite(MessagerImp* imp) : Messager(imp) {
}
virtual void Login(const std::string& username, const std::string& password) {
messagerImp->Connect();
// ...
}
virtual void SendMessage(const std::string& message) {
messagerImp->WriteText();
// ...
}
virtual void SendPicture(const Image& picture) {
messagerImp->DrawShape();
// ...
}
};
class MessagerPerfect : public Messager {
public:
MessagerPerfect(MessagerImp* imp) : Messager(imp) {
}
virtual void Login(const std::string& username, const std::string& password) {
messagerImp->PlaySound();
// ...
messagerImp->Connect();
// ...
}
virtual void SendMessage(const std::string& message) {
messagerImp->PlaySound();
// ...
messagerImp->WriteText();
// ...
}
virtual void SendPicture(const Image& picture) {
messagerImp->PlaySound();
// ...
messagerImp->DrawShape();
// ...
}
};
// ...
void Process() {
// 執行時裝配
MessagerImp* mImp = new PCMessagerImp();
Message* m = new MessagerPerfect(mImp);
}
轉載請註明出處