侯捷《C++/OOP/GP/DP》講座心得

>>>  小城故事吳儂軟語溫婉人心的力量  >>> 簡體     傳統

    很高興侯捷老師又來公司了,給我們上了四天非常生動的技術講座,受益匪淺,現在我簡要介紹一下我的學習心得,與大家分享。這次講座主要集中在《 C++/OOP/GP/DP 》主題,針對有一些編程基礎的工程師,對一些常用的代碼和設計做了非常通俗易懂的剖析,非常有幫助。當然更深入的理解還需要結合多種技術名著來學習,我結合我的理解以及自己的學習和開發的經驗介紹一下 C++/OO/Template 以及 Design Pattern的理會,考慮到講座的性質,我并不直述本次講座的內容,歡迎批評指正 J

 

    侯捷老師的講座基本是講述他多年來在 C++ 領域的研究成果,基本大部分都可以在他的書籍和網站上能讀到,但是考慮到最近幾年軟件技術的蓬勃發展,如 Design Pattern 的更廣泛應用,又有許多心得,基本上是較為泛的基礎的層面,并結合實際代碼和應用,對實際項目開發非常有益。下面我逐個主題泛泛地講一遍。

 

    面向對象中的合成( Composition )和繼承( Inheritance )關系

 

    通常擴展一個類的功能主要有兩種方式,一種是大家很熟悉的繼承( inheritance ),另一種就是合成( composition ),很多初學OO (面向對象)并有一些經驗都很容易搞混這個的區別,其實很簡單,繼承是解決 Is-a 的問題,而合成是解決 Has-a 的問題。比如說小鳥有兩個翅膀,就是合成,而鳥是一種飛禽,就是繼承了,設計一個“小鳥”的類,它繼承自”飛禽”,就具有“飛”的特性,但要用合成的方法“包含”一個“翅膀”的類才具有真正“飛”的功能。

    別看這兩個定義很簡單,其實很多人都犯過錯誤,包括 Java 類庫的設計者,他們就把 Properties 直接“繼承”自 Hashtable 了,這里其實應該用“合成”。

 

    講到合成,就應該說說聚合( Aggregation ),它是描述整體和局部的關系,合成其實是一種“強烈”的聚合,它與局部具有相同的生命周期,“容納”局部的“對象”,而聚合只是“容納”局部的一個“指針”。比如說,人和腦袋就是合成,而汽車與發動機就是聚合,改裝汽車可以任意替換更好的發動機,而人的腦袋就不行(目前是這樣:)

    聚合在 UML 中是以空心棱形的箭頭表示,合成是以實心棱形的箭頭表示。

 

    還有一種關系叫委托( Delegation ),委托是一種讓合成( composition )變得像繼承( inheritance )的復用能力一樣強大的方式。( a way of making composition as powerful for reuse as inheritance [Lie86, JZ91] )在委托中,兩個對象在處理一個請求的時候發生關聯:一個接收的對象委派操作給它的委托對象。這跟子類( subclass )延遲請求( deferring requests )給它的父類( parent class )來實現類似。但是在繼承里,一個被繼承的操作( inherited operation )通過 this 成員變量能夠經常引用到那個接收的對象。為了在委托里達到同樣的效果,接受者傳遞它自己給它的委托者,以便被委托的操作能夠引用到這個接收者。

 

    再說一下繼承( Inheritance ),它是將基類( base-class )所有一切(包括 private )都繼承下來,所以假如你想實現一個新的類,只想繼承一部分,就用合成( Composition )別用繼承。或者更進一步來講,如果你想改造一個類,想改造一些接口( interface ),也建議用合成,通過轉調內部對象的方法實現,別用虛函數( virtual function )。這是非常符合最基本的 OCP 設計原則( Open-Closed Principle ,開閉原則)的方式了。

 

    類的構造( Constructor )和析構( Destructor )

 

    類的構造和析構是最基礎的知識了,任何一個類的對象產生和銷毀都必須有這兩個步驟,但是它們是如何工作的,編譯器是如何制造缺省的ctor 和 dtor 的,估計少有人關注了。

    一個類的對象的產生,會依次從它最里面的類開始構造,同一個類會跟據內部類成員定義的順序依次構造。類對象的銷毀的過程則相反。基類的構造器會在用戶定義的 ctor 之前調用,基類的析構則是在用戶定義的 dtor 之后進行。熟悉這些過程,非常有利于設計出優秀的類庫,也不容易出現內存泄露和資源耗盡等問題。下面舉個例子更容易理解:

 

    class A { public: A(); ~A(); };

    class B { public: B(); ~B(); };

    class C { public: C(); ~C(); };

    class D : public A, B {

        public: D() { init(); } ~D() { release(); }

        private: void init(); void release(); C c;

    };

 

    上面的定義中 D 類的 ctor 構造過程如下:

    A::A();

    B::B();

    c.C::C();

    D::init();

 

    D 類的 dtor 析構過程如下:

    D::release();

    c.C::~C();

    B::~B();

    A::~A();

 

    更復雜的繼承關系以及多重繼承的構造和析構過程類似,有興趣的人可以寫程序測試:)

 

    還有一個問題,編譯器會在什么時候自動產生 ctor 和 dtor 的呢,又是如何產生的呢

    其實很簡單,當你沒有寫缺省構造函數( default constructor )和缺省析構函數( default destructor )的時候,編譯器就會給你自動生成一個,換句話說,任何類都有構造函數和析構函數,雖然有時候什么都不做,還有復制構造函數( copy ctor )也會自動生成。但是如何產生會跟你的類的成員有關。如果成員都是原生類型,還有如果類成員也全部為原生類型, ctor 將只會跟普通變量定義的初始化一樣,給一個初值, dtor 則什么都不做, copy ctor 則會使用內存復制( memcpy )的方式復制對象。如果成員包含一個或多個類成員,而且至少有一個類成員定義有缺省構造方法,則產生的 ctor 會依次調用每個成員的 ctor 。 dtor 和 copy-ctor 產生方法類似。(詳見《 Inside the C++ Object Model 》)

 

    多態( Polymorphism )和虛函數( Virtual function )

 

    多態是面向對象的基本特性, C++ 里是通過 virtual 關鍵詞來提供的,它是通過在類對象里加入 vtbl 虛函數表來實現的,這一點相信大部分程序員都很清楚,不過怎么做到多態功能估計了解的不多了。要詳細了解,還請閱讀《 Inside the C++ Object Model 》一書,下面簡單介紹一下原理。

 

    一般編譯都會給包含有 virtual function 的類頭部(有的編譯也會放到底部,比如 VC )增加一個成員 vptr 指針,指向一個 vtbl虛函數表,為定長數組,大小是所有帶 virtual 的函數數目再加 1 。虛函數指針從 vtbl[1] 開始,按照定義順序,指向特定的函數實現。如果子類定義了父類中帶 virtual 的函數,則 vtbl 相應的指針指向子類的函數實現,否則就指向父類的實現。另外再說明一點,其中 vtbl[0] 是有別的用途,用來存放類型信息,做 dynamic_cast 用途。

    仍以上面的例子為例,如下的代碼編譯器是如何處理:

 

    A *p = new D();     // up-cast

    p->vfunc1();         // 編譯器會轉化為如下代碼

(*(p->vptr))[n](p); // n 為編譯期確定的固定數,即相應 virtual function

// 所在位置

 

    需要牢記一點,總是讓 base class 擁有 virtual destructor 。因為當如下操作時

 

    delete p;

 

    如果 A 和 B 的析構函數不是虛函數,則不會調用子類 D 的 dtor ,就有可能造成內存泄露或者資源沒有釋放等嚴重問題。如果給 base class 加了 virtual dtor ,由于有多態的特性,就會自動調用 subclass 的 dtor ,接下來就會上面的介紹,依次調用各個 base class的 dtor ,因而就沒有問題了。

 

    C++ template 和 STL containers

 

    C++ template 即模板技術是實現泛型編程技術的,能夠使得寫一份代碼可以應用到類似用途的不同地方。模板技術其實原理比較簡單,但是使用還是比較復雜的,看看 STL 源碼就知道了,如果還不相信,再看看 Boost 代碼好了,會把你搞得暈頭轉向。候捷老師把這個技術講解得非常清楚易懂,還具體分析了 STL 里各個大組件的運作原理,我這里就不講述了,基本都是源碼的剖析,請閱讀候捷老師的《 STL 源碼剖析》一書。

 

    在講解 STL 中用模板如何實現 function class (實現函數功能的類,在 stl_functions.h )中,有這樣一段代碼

 

template <class _Operation>

class binder1st

  : public unary_function<typename _Operation::second_argument_type,

                          typename _Operation::result_type> {

protected:

  _Operation op;

  typename _Operation::first_argument_type value;

public:

  binder1st(const _Operation& __x,

            const typename _Operation::first_argument_type& __y)

      : op(__x), value(__y) {}

  typename _Operation::result_type

  operator()(const typename _Operation::second_argument_type& __x) const {

    return op(value, __x);

  }

};

 

    有人提出上面 _Operation op; 為什么不定義為引用,如 _Operation &op; 呢。我的想法如下,因為構造方法為

  binder1st(const _Operation& __x, // 這里為 const 類型

            const typename _Operation::first_argument_type& __y)

 

    傳入的參數為 const 類型,這時不應在本調用方法(這里是構造方法)之外使用引用或指針指向它,因為帶 const T &t 的參數一般情況都視為臨時對象,很有可能是在方法調用的時候臨時產生的,比如說自動轉型產生的臨時對象都是 const T & 類型,它的生命周期都在此方法調用期間內,方法調用結束即被銷毀,所以就不能在方法外部用引用或指針之類指向它了。舉例來說,可能比較容易理解,比如大家常用的string 類,假如有一個方法和調用如下:

 

    void func(const string &s);

    func("abcdfd");

 

    這個時候就會出現自動轉型行為,編譯器會做如下處理

 

    func(string("abcdfd"));

 

    即產生一個臨時的 string 對象,這個對象是以 const 類型傳入的。假如你的方法定義改成如下

 

    void func(string &s);

 

    現在大部分編譯器嚴格的處理都會報錯,以前的 VC6 就不會,但是好像最新的 VC2005 也報錯了。

    這是其中一個原因,還有一個原因我認為是 _Operation 類只是一個 function class ,沒有成員,所以做復制構造也不會有多大的開銷,基本不會影響效率。再加模板和 inline 方法的處理,編譯器經過優化,應該都不會產生臨時對象了,所以也不必用引用了。不過我覺得最重要是上面第一個原因。

 

    內存池和小對象分配器( memory pool, small object allocator )

 

    候捷老師在內存池方面也有很豐富的研究經驗,他基本將目前主流的內存池實作都剖析了一遍,介紹了它們各自的特點,以及如何與上層框架的配合。內存池是一個非常基礎也非常關鍵的底層庫,一般大型的框架自己都帶有一個內存池庫,比如 STL 、 MFC 等。即使在目前內存比較便宜的今天,內存資源也是最寶貴的系統資源之一,設計一個優秀的內存池對提高系統的效率和穩定性都非常有幫助,尤其是設計專門針對小內存對象(一般低于 128 字節)的分配器非常重要,因為這樣對象分配和釋放非常頻繁,只用簡單的 malloc() 和 free() 來處理非常影響效率,不是一個優秀的設計。下面我簡要介紹一下目前主流內存池設計的特點,以及我自己的想法,另外再加一個候捷老師沒提到 ACE 中的內存池管理器的設計特點。

 

    SGI STL 中的內存分配器( allocator )

 

    SGI STL 的 allocator 應該是目前設計最優秀的 C++ 內存分配器之一了,它的運作原理候捷老師在《 STL 源碼剖析》里講解得非常清楚。基本思路是設計一個 free_list[16] 數組,負責管理從 8 bytes 到 128 bytes 不同大小的內存塊( chunk ),每一個內存塊都由連續的固定大小( fixed size block )的很多 chunk 組成,并用指針鏈表串接起來。比如說

 

    free_list[3]->start_notuse->next_notuse->next_notuse->...->end_notuse;

 

    當用戶要獲取此大小的內存時,就在 free_list 的鏈表找一個最近的 free chunk 回傳給用戶,同時將此 chunk 從 free_list 里刪除,即把此 chunk 前后 chunk 指針鏈結起來。用戶使用完釋放的時候,則把此 chunk 放回到 free_list 中,應該是放到最前面的start_free 的位置。這樣經過若干次 allocator 和 deallocator 后, free_list 中的鏈表可能并不像初始的時候那么是 chunk 按內存分布位置依次鏈接的。假如 free_list 中不夠時, allocator 會自動再分配一塊新的較大的內存區塊來加入到 free_list 鏈表中。

    可以自動管理多種不同大小內存塊并可以自動增長的內存池,這是 SGI STL 分配器設計的特點。

 

    Loki 中的小對象分配器( small object allocator )

 

    Loki 的分配器與 SGI STL 的原理類似,不同之處是它管理 free_list 不是固定大小的數組,而是用一個 vector 來實現,因此可以用戶指定 fixed size block 的大小,不像 SGI STL 是固定最大 128 bytes 的。另外它管理 free chunks 的方式也不太一樣, Loki 是由一列記錄了 free block 位置等信息的 Chunk 類的鏈表來維護的, free blocks 則是分布在另外一個連續的大內存區間中。而且 free Chunks 也可以根據使用情況自動增長和減少合適的數目,避免內存分配得過多或者過少。

    Loki 的分配器使用也不太一樣,可以直接調用,如下

 

    SmallObjAllocator myAlloc(2048, 256); // 參數 1 為 chunk size

                                          // 參數 2 為 max fixed size block size

    // 可以用于小于 256 bytes 的各種大小內存的分配

    void *p1 = (void*)myAlloc.Allocate(20);

    void *p2 = (void*)myAlloc.Allocate(100);

    void *p3 = (void*)myAlloc.Allocate(256);

    void *p4 = (void*)myAlloc.Allocate(300); // 大于 256 將轉交給系統處理

    myAlloc.Deallocate(p1,20);

    myAlloc.Deallocate(p2,100);

    myAlloc.Deallocate(p3,256);

    myAlloc.Deallocate(p4,300);

 

    MFC 的 CPlex 和 CPtrList (扮演 memory pool 角色)

 

    CPlex 任務比較簡單,只負責管理一大塊 memory 并串接起來,用戶每次獲取都返回一大塊。分割由使用者(如 Collection classes, CFixedAlloc )將這一大塊切割為一個個小的內存塊。

    CPtrList 則負責管理這些切割后的小內存塊,這一點有點類似 Loki 中的 free Chunks ,不過要簡單多了。

    MFC 還有一個類叫 CFixedAlloc ,它是提供給應用類來分配固定大小(根據具體應用類的大小)的內存分配器。通過在應用類中定義DECLARE_FIXED_ALLOC(Foo) 和 IMPLEMENT_FIXED_ALLOC(Foo) 兩個宏來實現。

 

    Boost 的 object_pool

 

    Boost 中的 object_pool 也是一個可以根據用戶具體應用類的大小來分配內存塊的,也是通過維護一個 free nodes 的鏈表來管理的。可以自動增加 nodes 塊,初始是 32 個 nodes ,每次增加都以兩倍數向 system heap 要內存塊。 object_pool 管理的內存塊需要在其對象銷毀的時候才返還給 system heap 。

 

    ACE 中的 ACE_Cached_Allocator 和 ACE_Free_List

 

    ACE 框架中也有一個可以維護固定大小的內存塊的分配器,原理與上面講的內存池都差不多。它是通過在 ACE_Cached_Allocator 中定義個 Free_list 鏈表來管理一個連續的大內存塊的,里面包含很多小的固定大小的未使用的區塊( free chunk ),同時還使用ACE_unbounded_Set 維護一個已使用的 chuncks ,管理方式與上面講的內存池類似。也可以指定 chunks 的數目,也可以自動增長,定義大致如下所示:

 

template<class T>

class ACE_Cached_Allocator : public ACE_New_Allocator<T> {

public:

    // Create a cached memory pool with @a n_chunks chunks

    // each with sizeof (TYPE) size.

    ACE_Cached_Allocator(SIZET n_chunks = ACE_DEFAULT_INIT_CHUNKS);

    T* allocate();

    void deallocate(T* p);

private:

    // List of memory that we have allocated.

    Fast_Unbounded_Set<char *> _allocated_chunks;

    // Maintain a cached memory free list.

    ACE_Cached_Free_List<ACE_Cached_Mem_Pool_Node<T> > _free_list;

};

 

    設計模式

 

    最后一個主題重點講講設計模式,設計模式現在已經應用很廣泛了,可以說是無處不在。設計模式現在對程序員是非常的重要,甚至到了不懂設計模式就不算真正的程序員一樣。不過設計模式卻又是非常高階的理論,需要有多年的編程經驗才能真正領悟,所以學習起來非常頭痛。因為它道理非常簡單,但是卻非常抽象,候捷老師通過一大堆實際案例給我們逐個講述了幾個常用的模式的區別和用法。設計模式最經典最權威當屬著名的有字天書 GoF 的《 Design Patterns 》了,我結合自己學習和實踐的體會介紹一下幾個模式。

 

    結構型模式之 Composite (合成模式)

 

    GoF 的定義: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects. 翻譯為中文大致意思是:將對象 (s) 組成為樹狀結構,用以表示“局部 - 整體”的層次體系,使得讓 clients 可以以一致的方式對待“單個對象”和“合成對象”。

 

    比較典型的例子就是文件系統中“文件”和“目錄”的關系,還有 Windows 窗口系統也是,在一個窗口中還可以開另一個窗口,多個窗口組合成的窗口還可以當作一個窗口放入另一個窗口中,比如在 Word 中打開多個文檔就是這種情況。 Composite 模式的好處就是使得 clients調用簡單,可以用一致的接口處理單個對象或者多個單一對象組合成的對象。

 

    實例: Java swing library 中 Component , Label , Container 就是 Composite 模式的應用。其中 Label 和 Container 都繼承自 Component ,但是 C ontainer 中只是一個存放 Component 的數組,所以 Container 中就可以放很多 Component ,比如ScrollPane 就是繼承自 Container ,它可以放 Label ,還有 List , Scrollbar 等等,甚至還可以放一個 ScrollPane ,所以就達到了Composite 模式的效果,簡化了 client 的使用。

 

    結構型模式之 Decorator (裝飾模式)

 

    GoF 的定義: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. 翻譯為中文大致的意思是:以動態的方式給一個對象添加一些額外的職責,使得不必進行subclassing 就能擴展功能。

 

    Decorator 模式與 Composite 模式的區別就是它只內含一個 component object field ,而 Composite 則內含一個collection of component field 。 Decorator 負責將一個對象“裝飾”起來,做一些“改造或者擴展”,提供額外的功能,它只針對一個 class 。而 Composite 是一組“類似”的對象及其容納它們的容器一視同仁,使得 client 更簡單地處理單個對象和一組對象。它們目的不一樣。

 

實例: Java IO library 中 BufferedReader , Reader 之間使用的就是 Decorator 模式,其中 BufferedReader 繼承自Reader ,同時它內部含有一個 Reader 引用,它是通過另一個 Reader 對象構造而來,因此就為 Reader 提供更多的功能,如帶緩沖的Reader 。使用非常簡單,只需要如此定義:

 

Reader in = new BufferedReader(new FileReader("test.txt"));

 

就為文件讀取增加了帶緩沖的 IO 功能,非常方便。還可以多個 Decorator 的類組合使用,可以提供更強大的功能,多使用一下 Java IO library 就會體會到。

 

    行為模式之 Observer (觀察者模式)

 

    GoF 的定義: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

翻譯為中文大致意思是:在 objects 之間定義一個“一對多”的依賴關系,使得當這個 object 改變狀態時,所有依賴它的 objects 都能獲得通知并自動更新。

 

    Observer 是用于做“通知”用途的,就像“ publish-subscribe ”,它能夠做到注冊需要通知的對象,并自動通知它們來更新,它們都是被動地被通知,而不是主動觀察。

 

    實例: MFC 里 CView 和 CDocument 之間就是一個觀察者模式, CView 是 Observer 即觀察者, CDocument 是 Observable 即被觀察者,當 CDocument 修改后會自動通知所有的 CView 對象來自動更新它們顯示的內容,這一點可以用 Word 很容易試出來。還有最新的Windows 圖形界面框架 WinForm 中的窗口之間消息傳遞等用的也是 Observer 模式,一個窗口發生改變時會自動通知所有與它有關系的窗口,來自動更新信息等,這一點 Jeffrey Richter 可以作證 J

 

    行為模式之 Template Method (模板方法)

 

    GoF 的定義: Define the skeleton of an algorithm in an operation, deferring somesteps to subclasses. Template Method lets subclasses redefine certain steps ofan algorithm without changing the algorithm's structure. 翻譯為中文大致意思是:定義一個算法的骨干,延緩其中某些步驟以便在 subclasses 中定義它們。 Template Method 使得 subclasses 在不改變算法的體系結構的前提下得到重新定義算法內的某些步驟。

 

    Template Method 其實最常用了,在 C++ 里的應用就是使用 virtual function 實現的,給一個 base class 定義一個 virtual方法,但是不實現,而是在它的 subclasses 中實現它,這就是 Template Method 。這個模式的好處顯而易見,就是在你設計 base class的時候并不知道這個方法具體如何實現,但是卻需要在 base class 里某個地方需要調用它以完成一個完整的算法,這時候就是需要用這個模式了。

 

    實例:例子太多了,到處都是。

 

    行為模式之 Strategy (策略模式)

 

    GoF 的定義: Define a family of algorithms, encapsulate each one, and make theminterchangeable. Strategy lets the algorithm vary independently from client that use it. 翻譯為中文大致意思是:定義一族算法,把每個算法封裝起來,并使它們可以相互替換。 Stategy 使得算法給 client 使用的時候變得完全獨立。

 

    Strategy 模式完全符合 OCP ( Open-Closed Principle ,開閉原則),可以在不需要做 subclass 更不需要修改 base class的情況下在 Runtime 的時候擴展系統的功能,它不像 Template Method 需要 subclass 才能擴展新的功能。

 

    實例: Strategy 典型的應用是在薪資系統中,當定義 Employee 類的時候,如果使用 Template Method 模式,就無法更改員工領薪資的方式等,比如員工晉升的時候。這時候就需要用 Strategy 模式,在 Employee 中定義一個指針指向具體的 PaymentMethod 對象,如果需要更改領薪資的方式的時候,只需要將它指向新的 PaymentMehtod 實現就可以了。

 

    結構模式之 Adapter (適配器模式)

 

    GoF 的定義: Convert the interface of a class into another interface clients expect.Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. 翻譯為中文大致意思是:將一個 class 的接口轉換為另外一種clients 所期望的接口,使得這些 classes 可以更好地協同工作起來,而不至于因為別的不兼容的問題所影響。

 

    Adapter 模式屬于結構型模式,需要有 Adaptee (被適配者)和 Adaptor (適配器)兩個對象。一般情況下,我們通過繼承(Inheritance )或者合成( Composition )的方式擴展類的功能后,會產生很多接口形式不同的類,但是我們想用同樣的方式調用它們,又不想改造它們(否則違反 OCP 原則),怎么辦呢?這種情況下就需要用到 Adapter 模式了,這些被“改造”的類就叫做 Adaptee ,而我們就需要寫個 Adaptor 類來轉調它們,把調用的接口改成統一的形式。

 

    實例: Java IO library 里的 InputStreamReader 就是一個 Adapter ,它負責將 InputStream 對象轉換為 Reader 對象的接口形式,使得用戶可以像使用其它 Reader 一樣的方式來使用 InputStream 對象,比如:

 

InputStreamReader isr = new InputStreamReader(

new FileInputStream("test.txt"));

    BufferedReader br = new BufferedReader(isr);

    String line = br.readLine();

 

    這就做到了在不改造原來設計的類的接口的情況,擴展了類的應用范圍。一般情況下, Adapter 模式需要結合 Factory 模式和 Proxy模式來使用,使得接口的訪問更加一致性,更容易改造系統。

 

    結構模式之 Facade (外觀模式)

 

    GoF 的定義: Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level interface that makes the subsystem easier to use. 翻譯為中文大致意思是:為一個子系統的一批接口提供一個統一標準的接口, Facade定義更高層次的接口,使得子系統更容易使用。

 

    Facade 模式我感覺是一個更高層次的 Adapter ,它是用來理順系統之間的關系,降低系統間的耦合度的常用方法,其實我們可能在設計系統的時候就在不知不覺地應用這個模式了。大部分將業務邏輯層和表示層分離的框架都是應用 Facade 模式實現的,使得上層使用者完全不必理會底層的實現,卻又有統一的使用界面。

 

    實例: J2EE EJB 中 Session Bean 就是一種典型的 Facade 模式,即 Session Facade 。它完全將業務對象封裝起來, EJB 客戶端訪問 Session Bean 來代替訪問業務對象。這就大大簡化了 EJB 的系統結構, Session Bean 就是相當于一個外觀,也相當于一個總管,把業務對象都管理起來,不讓客戶端直接“接觸”到它們。

 

    《設計模式》一書中還有很多模式這里就不一一介紹了。其實我們大家都可以自己設計一種模式,但是肯定沒有 GoF 的那 23 個著名了。不過現在還有很多別的也很有名的模式,比如說 Wrapper 模式(相當于 Decorator )、 DAO 模式( Data Access Object 模式, OR 映射系統里常用到)、 MVC 模式(著名的 Model-View-Control 架構模式)等等。我的一個觀點,只有大量地實踐,持續地學習,就會不斷提升自己設計模式的層次,如果想靠一兩年的學習就想精通它,是不可能的任務 J

 

    寫了這么多,終于寫完了,呵呵。寫完后,有兩點體會: 1, 寫源碼剖析類的文章,就像是給代碼做翻譯,把代碼翻譯成圖片或文字。 2 ,寫 OO/DP 之類理論的文章,則相當于創造,不同的人有不同的體會,技術學習層面不同的理解也不同。還是那句話:理論需要從實踐中來,多學習多實踐。歡迎指正!

 

最后我列幾本候捷老師推薦的書:

    1 , C++ 程序員必備的書(我以為,即時你不看,也建議備上,充門面也好,爭吵也好都很用)

       a) 《 C++ Programming Language 》 Bjarne Stroustrup

       b) 《 C++ Primer 》 Stanley B Lippman

       c) 《 Effective C++ 》 Scott Meyers

       d) 《 Design Patterns 》 GoF

    2, 提高或學習的書

       a) 《 Inside The C++ Object Model 》 Stanley B Lippman

       b) 《 Thinking in C++ 》 Bruce Eckel

       c) 《 More Effective C++ 》 Scott Meyers

       d) 《 STL 源碼剖析》侯捷

       e) 《 Modern C++ Design 》 Andrei Alexandrescu

       f) 《 Small Memory Software - Patterns for memory management 》

 

 

  

 

-- 適合讀者: C++ 中高級程序員

-- 作者: naven 博客: http://www.cppblog.com/javenstudio/ 2006年12月28日


作者: naven 2013-12-03 09:26:32

[新一篇] 長時間工作意味著什么

[舊一篇] 被詛咒的程序員的七宗罪
回頂部
寫評論


評論集


暫無評論。

稱謂:

内容:

驗證:


返回列表