Grasp Design Pattern

Information about Grasp Design Pattern

Published on July 9, 2014

Author: vinsonhsieh

Source: authorstream.com

Content

Grasp Design Pattern: Grasp Design Pattern -Vinson Hsieh PowerPoint Presentation: Template Method and Visitor Patterns 設計模式方面的經典著作是 GoF 的 Design Patterns 。但是那本書有一個缺點 ﹐ 不好懂。 從風格上講 ﹐ 該書與其說是為學習者而寫作的教程範本 ﹐ 還不如說是給學術界人士看 的學術報告 ﹐ 嚴謹有餘 ﹐ 生動不足。 實際上 Design Pattern 並非一定是晦澀難懂的 ﹐ 通過生動的例子 ﹐ 一個中等水平的 C++ 學習者完全可以掌握基本用法 ﹐ 在自己的編程實踐中使用 ﹐ 得到立竿見影的功效。 領悟設計模式 PowerPoint Presentation: Template Method class Shape { public: void read( std::istream & ); void write( std::ostream & ) const; virtual ~ Shape(); protected: virtual void do_read( std::istream & ); virtual void do_write( std::ostream & ) const; private: virtual std::string classID() const = 0; // 要 serialize 的 Client object ID, // 使 read 完後 , 有機會可以 new Object } 1. virtual destructor 有必要嗎 ? 成員函數 read 和 write 是非虛擬的 ﹐ 它們肯定是調用 protected 部份 do_read/do_write 虛擬成員函數來完成實際的工作 -template method 模式 2. 有一個 private pure virtual function 能工作嗎 ? child 能 override private 函數嗎 ﹖ 一個有 Serialize 功能的介面 ( 找看看有沒有錯 ?) Virtual Destructor: Virtual Destructor class A { int x; public: ~A() { x = 0; } }; class B : public A { int y; public: ~B() { y = 0; } }; main() { B* pb = new B(); delete pb; A* pa = new B(); delete pa; } When you try to delete a derived class object through a bass class pointer and base class has a non-virtual destructor , the results are undefined. ( 無 virtual table) main() { A* pa = new B(); delete pa; B* pb = new B(); delete pb; } virtual ( 有 virtual table) PowerPoint Presentation: Template Method Access right 跟一個函數是不是 virtual 的根本沒關係。 外部呼叫與往上層呼叫跟往下層呼叫 ? virtual 往下層呼叫 3. 為何把 std::string classID() const 設為 private and pure virtual ? void Shape ::write(std::ostream &Archive) const { Archive << classID() << std::endl; do_write(Archive); } classID() 是一個實現細節 ﹐ 用來在保存對象時指示具象類的類型 ﹐child 必須覆蓋它 ﹐ 所以必須是純虛的。但是既然是實現細節且不想讓成員函數被其他的類調用 ﹐ 就應該設為 private 的。 2. 有一個 private pure virtual function 能工作嗎 ? child 能 override private 函數嗎 ﹖ PowerPoint Presentation: Template Method 4. 解釋一下為什麼 do_read() 和 do_write() 是 protected 的 ﹖ 因為 child 需要調用這兩個函數的實現來讀寫 class 內的 Data (or pass to other member class to do read/write) 5. 再解釋解釋為什麼不把它們設為 public 的 ﹖” 因為調用它們的時候必須以一種特定的方式進行並確保只會被 Template Method 呼叫 比如 write() 函數 ﹐ 必須先把 具象類的類型 ID 寫入 ﹐ 再把對象 Data 寫入 ﹐ 這樣讀取的 時候 ﹐ 負責生成對象的模塊首先能夠知道要讀出來的對象是什麼類型的 ﹐ 然後才能正確地從 stream 讀取對象信息。 Read and write must follow the same rule. PowerPoint Presentation: Template Method 就跟學習外國口語一樣 ﹐ 學習 C++ 也不光是掌握語法而已 ﹐ 還必須要掌握大量的慣用法。 指某種結構背後隱含的慣用法 比如一個類有 virtual destructor ﹐ 相當于告訴你說 ﹕‘ 嗨 ﹐ 來繼承我吧 ﹗’ 而如果一個類的 destructor 不是虛擬的 ﹐ 則相當於是在說 ﹕‘ 看在老天的份上 ﹐ 別繼承我。’ 同樣的 ﹐virtual 函數的訪問控制級別也具有隱含的意義 一個 protected virtual function 告訴你 ﹕ ‘ 你寫的 child 應該 ﹐ 哦 ﹐ 可以說是必須調用或 override 我的實現。’ ( 如必要就用純虛擬函數 ) 而一個 private virtual function 是在說 ﹕ ‘ 你不可以調用我的實現 , 但是 child 可以覆蓋 ﹐ 也可以不覆蓋我 ﹐ 隨你的便。’ ( 如必要就用純虛擬函數 ) DerivedClass::ClassID() { Parent::classID(); // …… . } PowerPoint Presentation: Template Method 6. 那麼 public virtual function 呢 ﹖ public virtual function 不容易改成 Template Method ( 盡量少用 ) class HardToExtend { public: virtual void f( ); }; void HardToExtend::f( ) { // Perform a specific action } 假設你發佈了這個 class 。在寫第二版時 ﹐ 需求有所變化 ﹐ 你必須改用 Template Method 。 可是這很困難 ﹐ 你知道為什麼 ﹖ PowerPoint Presentation: Template Method 由兩種可能的辦法。其一 ﹐ 將 f() 的實現代碼轉移到一個新的函數中 ﹐ 然後將 f() 本身設為 non-virtual 的 class HardToExtend { // possibly protected virtual virtual void do_f( ); public: void f( ); //Remove the virtual }; void HardToExtend::f( ) { // pre-processing do_f( ); // post-processing } void HardToExtend::do_f( ) { // Perform a specific action } PowerPoint Presentation: Template Method 然而你原來寫的 Child 都是企圖 override 或調用 函數 f() 而不是 do_f() 的 ﹐ 你必須改變所有的 Child 實現 ﹐ 只要你錯過了一個 Child﹐ 你的類層次就會染上 先知 Meyers 所說的‘精神分裂的行徑’。 [ 參見 Scott Meyers﹐Effective C++, Item 37 ﹐ 絕對不要重新定義繼承而來的非虛擬函數 ] class A { public: virtual void f( ); protected: virtual do_f(); }; class B : public A { public: virtual void f( ); }; B b; A* pa = &b; B* pb = &b; pa->f( ); // A::f( ); pb->f( ); // B::f( ); 同一個 b 卻有兩個不同行為 … PowerPoint Presentation: 另一種辦法是將 f() 移到 private 區域 ﹐ 引入一個新的 non-virtual 函數 Template Method class HardToExtend { // possibly protected virtual void f( ); public: void call_f( ); }; 這會導致無數令人頭痛的問題。 首先 ﹐ 所有的客戶都企圖調用 f() 而不是 call_f() 。 更有甚者 ﹐ 改完 call_f() 後因為 大部份 Child 的 f() 可能都還 放在 public 區域中 ﹐ 這樣 Client 用戶可以訪問到你本來想保護的細節。 PowerPoint Presentation: Template Method 對待虛函數要像對待數據成員一樣 ﹐ 把它們設為 private 的 ﹐ 直到設計上要求使用更寬鬆的訪問控制再來調整。 要知道由 private 入 public 易 ﹐ 由 public 入 private 難啊 ﹗ 這裏所表達的思想具有一定的顛覆性 ﹐ 因為我們太容易在 Base class 中設置 public virtual function 了 ﹐ Java 中甚至專門為這種做法建立了 interface 機製 ﹐ 現在竟然說這不好 ﹗ 一時間真是接受不了。 這裏並不是一昧地反對 public virtual function ﹐ 只是在 template method 大背景下 給出上述原則。 雖然這個原則在一般的設計中也是值得考慮的 ﹐ 但是主要的應用領域還是在 template method 模式中。當然 ﹐ template method 是一種非常有用和常用的模式 ﹐ 因此也決定了本原則具有廣泛的意義。 PowerPoint Presentation: Visitor 我想在一個已發行的類層次 (class hierarchy) 中增加一個新的虛函數 ﹐ 但是這個已發行類層次是由另外一幫人維護的 ﹐ 其他人碰都不能碰 . 但可以讓它反過來訪問我吧 ??? 把我想做的 Plug in 上去 . 反過來我們要設計一個可以讓人 Plug in function 的 class. class Personnel { public: virtual void Pay ( /*...*/ ) = 0; virtual void Promote( /*...*/ ) = 0; virtual void Accept ( PersonnelV & ) = 0; // ... other functions ... }; class Officer : public Personnel { /* override virtuals */ }; class Captain : public Officer { /* override virtuals */ }; class First : public Officer { /* override virtuals */ }; PowerPoint Presentation: 我想要增加一個函數 ﹐ 如果對象是船長 ( Captain ) 就這麼做 ﹐ 如果是大副 ( First Officer ) 就那麼做。 Virtual function 正是解決之道 ﹐ 在 Personnel 或者 Officer 中聲明它 ﹐ 而在 Captain 和 First 覆蓋 (override) 它。 糟糕的是 ﹐ 我不能增加這麼一個 虛函數 Visitor PowerPoint Presentation: Visitor “ 任何問題都可以通過增加間接層次的方法解決。 ” 那些 C 語言門徒才會使用 switch 語句處理不同的對象類型 /* A C program */ void f(struct someStruct *s) { switch(s->type) { case APPLE: /* do one thing */ break; case ORANGE: /* do another thing */ break; /* ... etc. ... */ } } PowerPoint Presentation: 學習 C++ 語言時 ﹐ 最重要的事情就是學習如何設計好的類層次 Visitor 應該設計一個 Fruit 基類 ﹐ 派生出 Apple 和 Orange ﹐ 用 virtual function 來作具體的事情。 但是 ﹐ 你應該知道 ﹐ 通過使用 virtual function ﹐ 你增加了一個間接層次 針對問題 ﹐ 你所需要的不就是一個新的虛函數嗎 ﹖ 避免使用 Switch 可是我們無權修改類層次 ﹐ 對吧 ! PowerPoint Presentation: Visitor 其實它的設計允許你增加新的 virtual function ﹐ 而不必煩勞 RTTI 。 你可以通過增加一個間接層次的辦法解決這個問題。 請注意 ﹐ Personnel::Accept void Personnel::Accept( PersonnelV & v ) { v.Visit( *this ); } void Officer::Accept ( PersonnelV & v ) { v.Visit( *this ); } void Captain::Accept ( PersonnelV & v ) { v.Visit( *this ); } void First::Accept ( PersonnelV & v ) { v.Visit( *this ); } “Visitor 的 Base Class 如下 ﹕” class PersonnelV /*Visitor*/ { public: virtual void Visit( Personnel& ) = 0; virtual void Visit( Officer& ) = 0; virtual void Visit( Captain& ) = 0; virtual void Visit( First& ) = 0; }; PowerPoint Presentation: Visitor 當我要利用 Personnel 類層次的 polymorphism 時 ﹐ 我只要調用 Personnel::Accept(myVisitorObject) 。 由於 Accept 是虛函數 ﹐ 我的 myVisitorObject.Visit() 會針對正確的對象類型調用 ﹐ 接著 , 根據 重載法則 ﹐ 編譯器會挑選最貼切的那個 Visit 來調用。 這不相當于增加了一個新的虛擬函數了嗎 ﹖ PowerPoint Presentation: class DoSomething : public PersonnelV { public: virtual void Visit( Personnel& ); virtual void Visit( Officer& ); virtual void Visit( Captain& ); virtual void Visit( First& ); }; void DoSomething::Visit( Captain& c ) { if( femaleGuestStarIsPresent ) c.TurnOnCharm( ); else c.StartFight( ); } void DoSomething::Visit( First& f ) { f.RaiseEyebrowAtCaptainsBehavior( ); } void f( Personnel& p ) { // 相當于 p.DoSomething( ) p.Accept( DoSomething( ) ); } int main( ) { Captain k; First s; f( k ); f( s ); } Visitor 利用重載模擬虛擬 PowerPoint Presentation: 如果剛剛讀過 Visitor 模式。 Visitor 只不過是允許若干對象之間相互訪問的模式 ﹐ 不是嗎 ﹖( 拜訪者先讓受訪者們接受 , 再讓受訪者一個個跟拜訪者做不同的訪談 ) Visitor 這是流行的錯誤理解。那個 V﹐ 毋寧說是 Visitor ﹐ 還不如說是 Virtual 更好。 這個 PNP 最重要的用途是允許在不改變類層次的前提下 ﹐ 向已經存在的類層次中 增加新的虛函數 PNP﹐Poor-Named Pattern , 沒起好名字的模式 PowerPoint Presentation: Visitor RTTI 給出一個解決方案 void f( Officer &o ) { if( dynamic_cast<Captain*>(&o) ) /* do one thing */ else if( dynamic_cast<First*>(&o) ) /* do another thing */ } int main() { Captain k; First s; f( k ); f( s ); } PowerPoint Presentation: 本文根據發表在 CUJ Expert Forum 上的兩篇文章編譯而成。 C/C++ User's Journal 是目前最出色的 C/C++ 語言專業雜誌 ﹐ 特別是在 C++ Report 閉刊之後 ﹐CUJ 的地位更加突出。 CUJ Expert Forum 是 CUJ 主辦的網上技術專欄 ﹐ 彙集 2000 年 10 月 以來 C++ 社群中頂尖專家的技術短文 ﹐ 並免費公開發佈 ﹐ 精彩紛呈 ﹐ 是每一個 C/C++ 學習者不可錯過的資料。 由 Jim Hyslop 和 Herb Sutter 主持的 Conversation 系列 ﹐ 是 CUJ Expert Forum 每期必備的精品專欄 ﹐ 以風趣幽默的對話形 式講解 C++ 高級技術 ﹐ 在 C++ 社群內得到廣泛讚譽。 這邊特別挑選兩篇設計模式方面的文章 ﹐ 介紹給大家。 Template Method and Visitor Patterns PowerPoint Presentation: State + Template Method + Factory Method (OLE Register ,Recalculate Scroll Bars Position) PowerPoint Presentation: Factory Method PowerPoint Presentation: Window Client MOtifWindow PMWindow ScrollBar MotifScorllBar PMScrollBar WidgetFactory createScrollBar() CreateWindow() MotifWidgetFactory PMWidgetFactory createScrollBar() CreateWindow() createScrollBar() CreateWindow() Abstract Factory

Related presentations


Other presentations created by vinsonhsieh

Pepsi Refresh
15. 07. 2014
0 views

Pepsi Refresh

Build a Bear - Marketing
15. 07. 2014
0 views

Build a Bear - Marketing

談判
15. 07. 2014
0 views

談判

MFC Message Routing
09. 07. 2014
0 views

MFC Message Routing