在軟體元件的設計中,如果責任劃分的不清晰,使用繼承得到的結果往往是隨需求的變化,子類急劇膨脹,同時充斥著重複**,這時候的關鍵是劃清責任。在某些情況下我們可能會「過度地使用繼承來擴充套件物件的功能」,由於繼承為型別引入的靜態特質,使得這種擴充套件方式缺乏靈活性;並且隨著子類的增多(擴充套件功能的增多),各種子類的組合(擴充套件)會導致更多子類的膨脹。典型模式
- decorator
- bridge
那麼如何使「物件功能的擴充套件」能夠根據需要動態的實現?同時避免「擴充套件功能的增多」帶來的子類膨脹問題?從而使得任何「功能擴充套件變化」所導致的影響將為最低。
問題描述:設計一組與流相關的類,首先定義乙個抽象基類stream,之後繼承各種,如filestream,networkstream,memorystream。之後對流進行擴充套件操作,如加密操作,拷貝操作等。
//業務操作
class stream
};//主體類,檔案流
class filestream: public stream
virtual void seek(int position)
virtual void write(char data)
};//網路流
class networkstream :public stream
virtual void seek(int position)
virtual void write(char data)
};//記憶體流
class memorystream :public stream
virtual void seek(int position)
virtual void write(char data)
};//擴充套件操作,加密檔案流
class cryptofilestream :public filestream
virtual void seek(int position)
virtual void write(byte data)
};//擴充套件操作,加密網路流
class cryptonetworkstream : public networkstream
virtual void seek(int position)
virtual void write(byte data)
};//擴充套件操作,加密記憶體流
class cryptomemorystream : public memorystream
virtual void seek(int position)
virtual void write(byte data)
};//擴充套件的操作,快取檔案流
class bufferedfilestream : public filestream;
//擴充套件的操作,快取網路流
class bufferednetworkstream : public networkstream;
//擴充套件的操作,快取記憶體流
class bufferedmemorystream : public memorystream
//擴充套件的操作,加密緩衝檔案流
class cryptobufferedfilestream :public filestream
virtual void seek(int position)
virtual void write(byte data)
};void process()
分析:
以上例子是關於:對各種流的操作的,開始只有三個要求(檔案流filestream,網路流networkstream,記憶體流memorystream),然後這三個類都繼承於乙個抽象類(stream);之後提出各種需求,需要進行加密操作,快取操作等等。因此就有了各種擴充套件情況。下圖可以看出其關係。
什麼問題呢?可以看出以上的**存在大量的**冗餘(比如:加密操作都是一樣的,無論是針對檔案流還是網路流,加密檔案流cryptofilestream的讀操作和加密網路流的都操作都是先加密再讀),也就是一樣的**。再看看對cryptofilestream,cryptonetworkstream,cryptomemorystream進行一部分更改(將繼承改為組合)的**(只看讀操作)
class cryptofilestream :
};class cryptonetworkstream
};class cryptomemorystream
};
上面的**是不是很像,其中各個類新增的成員(filestream stream,networkstream stream,memorystream* stream)是不是可以進一步更改為stream* stream就可以了。更改完的**如下
class cryptofilestream :
};class cryptonetworkstream
};class cryptomemorystream
};
編譯時一樣,執行時不一樣絕大多數設計模式的原理,執行時讓他變化(用多型來支援其變化)。
這樣做完後,你發現這三個類是不是一模一樣,那只需要乙個類就行了(妙!!!)。這樣就消除了重複性,優化的**如下
class cryptofilestream :
};
上面**,但你有沒有發現乙個問題,cryptofilestream裡的read憑什麼是虛函式,因此必須得繼承基類(是為了完善介面規範),因此修改如下
class cryptostream: public stream
virtual char read(int number)
virtual void seek(int position)
virtual void write(byte data)
};
這樣cryptofilestream既有個基類的字段,也繼承基類;同樣buffer操作的**優化同上訴方法一樣。
這樣改完後 ,就可以這樣使用
void process()
執行時裝配什麼意思呢?編譯時不存在快取檔案流,什麼加密檔案流等等,沒有那樣的類,執行時可以通過組合裝配起來滿足需求。這就是裝飾的含義,裝飾是附著在其他物件上圖示如下
以上做法已經很完善了。但是,如果某乙個類的子類有同樣的字段時,應該往上提。提到哪?
方法一:提到基類。但是filestream不需要這個字段。提到基類不合適。
因此需要設計中間類,見下的第三個版本
//業務操作
class stream
};//主體類
class filestream: public stream
virtual
void seek(int position)
virtual
void write(char data)
};class networkstream :public stream
virtual
void seek(int position)
virtual
void write(char data)
};class memorystream :public stream
virtual
void seek(int position)
virtual
void write(char data)
};//擴充套件操作,中間類
decoratorstream: public stream
};class cryptostream: public decoratorstream
virtual
char read(int number)
virtual
void seek(int position)
virtual
void write(byte data)
};class bufferedstream : public decoratorstream
//...
};
當然,物件導向設計原則 裡就有條」用組合代替繼承」
動態(組合)地給乙個物件增加乙個額外的職責。就增加功能而言。decorator模式比生成子類(繼承)更為靈活(消除重複**&減少子類個數)
結構圖如下
C 模式設計 Decorator 裝飾模式
需要設計圓 矩形兩種形狀,同時設計這兩種形狀的紅色 藍色版本,按照傳統的思路,使用繼承的方式,則如下圖 按這種方式,當需要的形狀和顏色種類增多時,所定義的類的數量將會急劇增多,而且類之間會有很多重複的 如果運用裝飾模式的思想,依照 組合優先於繼承 原則,將 顏色 單獨定義為乙個 裝飾類 該類也繼承自...
Decorator設計模式
雖然設計模式分得太細會有過度的趨勢,decorator某種程度上也是一種facade模式。但是實現起來還是比較漂亮的。而後面那個人的class arlist list,ilist的方法就不是decorator。它沒有乙個內部的list。這樣 however,now all of list s met...
設計模式 decorator模式
裝飾者模式體現了 敏捷開發思想中的 對類要 開放擴充套件,關閉修改.例子 乙個person主類 若干裝飾品類 紅衣服,藍衣服,藍鞋子,紅鞋子 測試 new乙個person 給他穿上紅衣服藍鞋子 code person介面 public inte ce ipersonperson類 package c...