二、結構圖
(1)class adapter(2)object adapter
三、實現
和其他很多模式一樣,學習設計模式的重點是學習每種模式的思 想,而不應拘泥於它的某種具體結構圖和實現。因爲模式是靈活的,其實現可以是千變萬化的,只是所謂萬變不離其宗。 在STL中大量運用了Adapter模式,象function adapter、iterator adpter,它們與這裏說的adapter結構並不一樣,但思想是一樣的。具體的介紹可到侯捷網站上找相關文章,他講得非常好。四、示例代碼
(1)class adapter
namespace DesignPattern_Adapter { // class Adaptee class Adaptee { public: void SpecialRequest() {} } ; // class Target class Target { public: virtual void Request() = 0 ; } ; // class Adapter class Adapter : public Target, private Adaptee { public: virtual void Request() { SpecialRequest() ; } } ; } 客戶端代碼: { using namespace DesignPattern_Adapter ; Target *p = new Adapter() ; p->Request() ; //實際上調用的是Adaptee::SpecialRequest() } |
(2)object adapter namespace DesignPattern_Adapter
{ // class Adaptee class Adaptee { public: void SpecialRequest() {} } ; // class Target class Target { public: virtual void Request() = 0 ; } ; // class Adapter class Adapter : public Target { public: virtual void Request() { _adaptee.SpecialRequest() ; } private: Adaptee _adaptee ; } ; } 客戶端代碼: { using namespace DesignPattern_Adapter ; Target *p = new Adapter() ; p->Request() ; //實際上調用的是Adaptee::SpecialRequest() } |
六、實例
(1)STL中的Class Adapter STL中的Adapter Class包括:a.stack(對應的adaptee是deque)。b.queue(對應的adaptee是deque)。 c.priority_queue(對應的adaptee是vector)。 下面是從VC中的< stack >拷出的stack的類定義:
template class _Container = deque<_Ty> > class stack { // LIFO queue implemented with a container public: typedef _Container container_type; typedef typename _Container::value_type value_type; typedef typename _Container::size_type size_type; stack() : c() { // construct with empty container } explicit stack(const _Container& _Cont) : c(_Cont) { // construct by copying specified container } bool empty() const { // test if stack is empty return (c.empty()); } size_type size() const { // test length of stack return (c.size()); } value_type& top() { // return last element of mutable stack return (c.back()); } const value_type& top() const { // return last element of nonmutable stack return (c.back()); } void push(const value_type& _Val) { // insert element at end c.push_back(_Val); } void pop() { // erase last element c.pop_back(); } bool _Eq(const stack<_Ty, _Container>& _Right) const { // test for stack equality return (c == _Right.c); } bool _Lt(const stack<_Ty, _Container>& _Right) const { // test if this < _Right for stacks return (c < _Right.c); } protected: _Container c; // the underlying container }; |
關鍵之處在於_Container c,stack所有的操作都轉交給c去處理了。(這實際上就是前面所說的"object adapter",注意STL中的class adapter與上面所說的class adapter概念不完全一致) stack的使用方法很簡單,如下:
{ int ia[] = { 1,3,2,4 }; deque id(ia, ia+4); stack is(id); } |
(2)近日看了一篇文章“Generic< Programming >:簡化異常安全代碼”,原文出自http://www.cuj.com/experts/1812/alexandr.htm?topic= experts, 中文譯文出自"C++ View第5期"。 文章絕對一流,作者給出的代碼中也使用了Adaptor模式,也有一定代表性。我將其問題一般化,概括出以下示例:
問題:假設有幾個已有類,他們有某些共同的行爲,但它們彼此間是獨立的(沒有共同的基類)。如:
class T1 { public: void Proc() {} } ; class T2 { public: void Proc() {} } ; // ... |
如何以統一的方式去調用這些行爲呢?
解決方法1:很自然的會想到用模板,如:
template <class T> void Test(T t) { t.Proc() ; } |
的確不錯,但這隻適用於簡單的情況,有時情況是很複雜的,比如我們無法把類型放到模板參數中!
解決方法2:困難來自於這些類沒有共同的基類,所以我們就創造一個基類,然後再Adapt。
// class IAdaptor,抽象基類 class IAdaptor { public: virtual void Proc() = 0 ; } ; // class Adaptor template <class T> class Adaptor : public IAdaptor, private T //實現繼承 { public: virtual void Proc() { T::Proc() ; } } ; // 以統一方式調用函數Proc,而不關心是T1、T2或其他什麼類 void Test(const std::auto_ptr& sp) { sp->Proc() ; } 客戶端代碼: Test(std::auto_ptr(new Adaptor)) ; Test(std::auto_ptr(new Adaptor)) ; |
上例很簡單,用方法一中的模板函數就可以很好地解決了。下面是一個略微複雜一點的例子,根據參數類型來創建適當的對象:
class T1 { public: T1(int) { /*...*/ } void Proc() { /*...*/ } } ; class T2 { public: T2(char) { /*...*/ } void Proc() { /*...*/ } } ; // class IAdaptor,抽象基類 class IAdaptor { public: virtual void Proc() = 0 ; } ; // class Adaptor template class Adaptor : public IAdaptor, private T //實現繼承 { public: Adaptor(int n) : T(n) {} Adaptor(char c) : T(c) {} virtual void Proc() { T::Proc() ; } } ; class Test { public: Test(int n) : sp(new Adaptor(n)) {} Test(char c) : sp(new Adaptor(c)) {} void Proc() { sp->Proc() ; } private: std::auto_ptr sp ; } ; 客戶端代碼: Test t1(10) ; t1.Proc() ; Test t2('c') ; t2.Proc() ; |
C++設計模式之Abstract Factory |
2002-07-23· · ··COM集中營 |
一、功能
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
二、結構圖 類廠最基本的結構示意圖如下:在實際應用中,類廠模式可以擴充到很複雜的情況,如下圖所示: 三、優缺點 優點:(1)封裝創建過程。客戶不用知道類廠是如何創建類實例的,類廠封閉了所有創建的細節。這樣可選擇不同的創建方法,增加了靈活性。 (2)將客戶與具體類隔離,提高了各自的可重用性。 缺點:Factory類層次與具體類層次通常是平行的(即一一對應的)。增加一個具體類,一般也要相應地增加一個factory類,增加了系統複雜度。 四、實現 (1)Abstract Factory類中通常是一組Factory Method的集合。個人認爲與Factory Method模式沒有本質區別。 (2)通常可以把工廠作爲單件。五、示例代碼 namespace DesignPattern_AbstractFactory { class AbstractProductA {}; // Product A class ProductA1 : public AbstractProductA {}; class ProductA2 : public AbstractProductA {}; class AbstractProductB {}; // Product B class ProductB1 : public AbstractProductB {}; class ProductB2 : public AbstractProductB {}; class AbstractFactory { public: virtual AbstractProductA* CreateProductA() = 0 ;// 創建ProductA virtual AbstractProductB* CreateProductB() = 0 ;// 創建ProductB } ; class ConcreteFactory1 : public AbstractFactory { public: virtual AbstractProductA* CreateProductA() { return new ProductA1() ; } virtual AbstractProductB* CreateProductB() { return new ProductB1() ; } static ConcreteFactory1* Instance() { static ConcreteFactory1 instance ; return &instance ; } protected: ConcreteFactory1() {} private: ConcreteFactory1(const ConcreteFactory1&) ; ConcreteFactory1& operator=(const ConcreteFactory1&) ; } ; class ConcreteFactory2 : public AbstractFactory { public: virtual AbstractProductA* CreateProductA() { return new ProductA2() ; } virtual AbstractProductB* CreateProductB() { return new ProductB2() ; } static ConcreteFactory2* Instance() { static ConcreteFactory2 instance ; return &instance ; } protected: ConcreteFactory2() {} private: ConcreteFactory2(const ConcreteFactory2&) ; ConcreteFactory2& operator=(const ConcreteFactory2&) ; } ; } 客戶端代碼: { using namespace DesignPattern_AbstractFactory ; // 第一種創建方法 AbstractFactory *pFactory = ConcreteFactory1::Instance() ; AbstractProductA *pProductA = pFactory->CreateProductA() ; AbstractProductB *pProductB = pFactory->CreateProductB() ; // 第二種創建方法 pFactory = ConcreteFactory2::Instance() ; pProductA = pFactory->CreateProductA() ; pProductB = pFactory->CreateProductB() ; } 六、實例 最早知道類廠的概念是在COM中,但當時也沒想到這是如此重要的一種模式,在許多其他模式中都可以用到類廠模式。 COM中不能直接創建組件,這也是由COM的一個特性決定的:即客戶不知道要創建的組件的類名。 |
C++設計模式之Singleton | |||
2002-07-26· · ··COM集中營 | |||
一、功能
保證一個類僅有一個實例。 二、結構圖 三、優缺點 Singleton模式是做爲"全局變量"的替代品出現的。所以它具有全局變量的特點:全局可見、貫穿應用程序的整個生命期,它也具有全局變量不具備的性質:同類型的對象實例只可能有一個。 四、實現 教科書上的Singleton定義如下:
(1)因爲返回的是指針,爲防止用戶調用delete函數,可把static Singleton *_instance;改爲在Instance()中定義static Singleton _instance。這樣顯然更安全,同時也具有lazy initialization的特性(即第一次訪問時才創建)。 (2)假設需要從Singleton派生子類,而子類也需要有同 樣的性質,既只能創建一個實例。我覺得,這很難辦。根本原因在於Instance()函數不是虛函數,不具有多態的性質。一種常用方法是把 Instance()函數移到子類中,這時就只能用static Singleton *_instance,而不能用static Singleton _instance了,除非把_instance也要移到子類,無論怎麼做都不優雅。另一種方法是用模板。具體用什麼方法,只能根據實際情況權衡。 五、示例代碼 (1)沒子類的情況
(2)有子類的情況
|
C++模式開發之Bridge | |
2002-07-29· · ··COM集中營 | |
一、功能
將抽象部分與它的實現部分分離,使它們都可以獨立地變化。
二、結構圖 三、示例代碼
四、實例 (1)創建可以在X Window System和IBM的Presentation Manager系統中都可以使用的窗口。(書上的例子)Bridge的魅力在於抽象和實現之間是鬆散的關係,它們之間可以進行隨意組合。如上圖中,就有IconWindow+ XWindowImp、TransientWindow+XWindowImp、IconWindow+PMWindowImp、 TransientWindow+PMWindowImp四種組合。 |
C++模式設計之Builder | |
2002-07-30· · ··COM集中營 | |
一、功能
將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
二、結構圖 各類之間的交互關係如下圖所示: 三、示例代碼
四、實例 (1)例子一。如下圖所示: 上圖的功能是是把一個RTF文件轉換爲多種正文格式。RTFReader進行語法分析,然後將所有的token串逐一轉換。可見builder就是一步步地把各個部分組裝爲一個整體。它封閉了組裝的方法,組裝出來的對象也大相徑庭。 |
C++設計模式之Prototype | |
2002-08-01· · ··COM集中營 | |
一、功能
用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。
二、結構圖 三、優缺點 優點:複製自身。客戶不知道需要對象的實際類型,只需知道它的抽象基類即可。(即有繼承樹的情況) 缺點:必須先有一個對象實例(即原型)才能clone。 四、示例代碼
五、實例 在一個圖形編輯器中,每一個圖形元素,如線、圓、文字等都應該支持拷貝操作,即點中圖形,按下Ctrl+C,再按下Ctrl+V後就會複製一個新的圖形。顯然這是一種clone操作。所以在每個從Graphic派生出的圖形子類都應運用Prototype模式,加上Clone操作。 |
C++設計模式之Factory Method | ||
2002-08-05· · ··COM集中營 | ||
一、功能
定義一個用於創建對象的接口,讓子類決定實例化哪一個類。Factory Method 使一個類的實例化延遲到其子類。 二、結構圖 三、實現 (1)在某些情況下,比如僅僅爲了創建適當的Product對象而派生新的Creator子類,並且創建不同Product的方法一致時,可以考慮用模板代替繼承。如:
模板與繼承的本質區別之一是:模板:行爲不依賴於類型。繼承:行爲依賴於類型。(Effective C++ Item 41) 事實上,在很多模式中都存在着可以用模板代替繼承的情況,其根本原因就在於子類的行爲是一致的。 四、示例代碼
這裏的CreateProduct其實也是一個Template Method。 五、實例 Factory Method的運用太廣泛了,它經常運用在其它模式中,其實例舉不勝數。 (1) MFC中的CDocument類就包含了類似於上圖CApplication中的三個函數。這裏的CreateDocument就是一個factory method,因爲它負責創建一個文檔對象。 (2) 當一個類將它的一些職責委託給一個獨立的類時,就產生了平行類層次。上圖中Figure和Manipulator就是平 行類層次,Figure代表一些圖形元素,如線、文字等,Manipulator表示作用於這些圖形元素的操作,如拖拉、移動、選中等。如果這些操作所需 要的狀態信息並不需要保存在Figure中,那麼把Figure和Manipulator分成兩個類層次是個好主意。這裏的CreateManipulator就是一個factory method。 |
C++設計模式之Composite | ||
2002-08-06· · ··COM集中營 | ||
一、功能
表示“部分-整體”關係,並使用戶以一致的方式使用單個對象和組合對象。
二、結構圖 上圖中,也可以做些擴展,根據需要可以將Leaf和Composite做爲抽象基類,從中派生出子類來。 三、優缺點 優點:對於Composite模式,也許人們一開始的注意力會集中在它是如何實現組合對象的。但Composite最重要之處在於用戶並不關心是組合對象還是單個對象,用戶將以統一的方式進行處理,所以基類應是從單個對象和組合對象中提出的公共接口。 缺點:Composite最大的問題在於不容易限制組合中的組件。 四、實現 有時需要限制組合中的組件,即希望一個Composite只能有某些特定的Leaf。這個問題我是 用多繼承和動態類型轉換來解決的。假如組合對象Composite1只能包含單個對象ConcreteLeaf1,Composite2可以包含單個對象 ConcreteLeaf1和ConcreteLeaf2。如下圖所示:上圖中的類層次比較多,使用了AbstractLeaf1和AbstractLeaf2,但沒使用 AbstractComposite1和AbstractComposite2,這個並不重要,也可以把AbstractLeaf1和 AbstractLeaf2去掉,這個並不重要,可以根據具體情況決定要不要。 簡單的代碼實現如下:
有兩點需要注意,一是因爲用了多繼承,所以需要使用virtual inheritance。二是要用dynamic_cast來判斷是否允許組合該組件。 五、示例代碼
六、實例 (1)JUnit中就用的是Composite模式。 |