• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Javen-Studio 咖啡小屋

            http://javenstudio.org - C++ Java 分布式 搜索引擎
            Naven's Research Laboratory - Thinking of Life, Imagination of Future

              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              24 隨筆 :: 57 文章 :: 170 評論 :: 4 Trackbacks

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

            ????????????????? ?????????????????????????????????????????????????????????????????????????????????????????????? ——— 作者: naven

            ?

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

            ?

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

            ?

            ??? 面向?qū)ο笾械暮铣桑?/span> Composition )和繼承( Inheritance )關(guān)系

            ?

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

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

            ?

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

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

            ?

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

            ?

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

            ?

            ??? 類的構(gòu)造( Constructor )和析構(gòu)( Destructor

            ?

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

            ??? 一個類的對象的產(chǎn)生,會依次從它最里面的類開始構(gòu)造,同一個類會跟據(jù)內(nèi)部類成員定義的順序依次構(gòu)造。類對象的銷毀的過程則相反?;惖臉?gòu)造器會在用戶定義的 ctor 之前調(diào)用,基類的析構(gòu)則是在用戶定義的 dtor 之后進行。熟悉這些過程,非常有利于設(shè)計出優(yōu)秀的類庫,也不容易出現(xiàn)內(nèi)存泄露和資源耗盡等問題。下面舉個例子更容易理解:

            ?

            ??? 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 構(gòu)造過程如下:

            ??? A::A();

            ??? B::B();

            ??? c.C::C();

            ??? D::init();

            ?

            ??? D 類的 dtor 析構(gòu)過程如下:

            ??? D::release();

            ??? c.C::~C();

            ??? B::~B();

            ??? A::~A();

            ?

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

            ?

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

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

            ?

            ??? 多態(tài)( Polymorphism )和虛函數(shù)( Virtual function

            ?

            ??? 多態(tài)是面向?qū)ο蟮幕咎匦裕?/span> C++ 里是通過 virtual 關(guān)鍵詞來提供的,它是通過在類對象里加入 vtbl 虛函數(shù)表來實現(xiàn)的,這一點相信大部分程序員都很清楚,不過怎么做到多態(tài)功能估計了解的不多了。要詳細了解,還請閱讀《 Inside the C++ Object Model 》一書,下面簡單介紹一下原理。

            ?

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

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

            ?

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

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

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

            // 所在位置

            ?

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

            ?

            ? ??delete p;

            ?

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

            ?

            ??? C++ template STL containers

            ?

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

            ?

            ??? 在講解 STL 中用模板如何實現(xiàn) function class (實現(xiàn)函數(shù)功能的類,在 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; 呢。我的想法如下,因為構(gòu)造方法為

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

            ??????????? const typename _Operation::first_argument_type& __y)

            ?

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

            ?

            ??? void func(const string &s);

            ??? func("abcdfd");

            ?

            ??? 這個時候就會出現(xiàn)自動轉(zhuǎn)型行為,編譯器會做如下處理

            ?

            ??? func(string("abcdfd"));

            ?

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

            ?

            ??? void func(string &s);

            ?

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

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

            ?

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

            ?

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

            ?

            ??? SGI STL 中的內(nèi)存分配器( allocator

            ?

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

            ?

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

            ?

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

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

            ?

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

            ?

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

            ??? Loki 的分配器使用也不太一樣,可以直接調(diào)用,如下

            ?

            ??? SmallObjAllocator myAlloc(2048, 256); // 參數(shù) 1 chunk size

            ????????????????????????????????????????? // 參數(shù) 2 max fixed size block size

            ??? // 可以用于小于 256 bytes 的各種大小內(nèi)存的分配

            ??? 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 將轉(zhuǎn)交給系統(tǒng)處理

            ??? 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 )將這一大塊切割為一個個小的內(nèi)存塊。

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

            ??? MFC 還有一個類叫 CFixedAlloc ,它是提供給應用類來分配固定大?。ǜ鶕?jù)具體應用類的大?。┑膬?nèi)存分配器。通過在應用類中定義 DECLARE_FIXED_ALLOC(Foo) IMPLEMENT_FIXED_ALLOC(Foo) 兩個宏來實現(xiàn)。

            ?

            ??? Boost object_pool

            ?

            ??? Boost 中的 object_pool 也是一個可以根據(jù)用戶具體應用類的大小來分配內(nèi)存塊的,也是通過維護一個 free nodes 的鏈表來管理的??梢宰詣釉黾?/span> nodes 塊,初始是 32 nodes ,每次增加都以兩倍數(shù)向 system heap 要內(nèi)存塊。 object_pool 管理的內(nèi)存塊需要在其對象銷毀的時候才返還給 system heap 。

            ?

            ??? ACE 中的 ACE_Cached_Allocator ACE_Free_List

            ?

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

            ?

            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;

            };

            ?

            ??? 設(shè)計模式

            ?

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

            ?

            ??? 結(jié)構(gòu)型模式之 Composite (合成模式)

            ?

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

            ?

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

            ?

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

            ?

            ??? 結(jié)構(gòu)型模式之 Decorator (裝飾模式)

            ?

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

            ?

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

            ?

            實例: Java IO library BufferedReader , Reader 之間使用的就是 Decorator 模式,其中 BufferedReader 繼承自 Reader ,同時它內(nèi)部含有一個 Reader 引用,它是通過另一個 Reader 對象構(gòu)造而來,因此就為 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 之間定義一個“一對多”的依賴關(guān)系,使得當這個 object 改變狀態(tài)時,所有依賴它的 objects 都能獲得通知并自動更新。

            ?

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

            ?

            ??? 實例: MFC CView CDocument 之間就是一個觀察者模式, CView Observer 即觀察者, CDocument Observable 即被觀察者,當 CDocument 修改后會自動通知所有的 CView 對象來自動更新它們顯示的內(nèi)容,這一點可以用 Word 很容易試出來。還有最新的 Windows 圖形界面框架 WinForm 中的窗口之間消息傳遞等用的也是 Observer 模式,一個窗口發(fā)生改變時會自動通知所有與它有關(guān)系的窗口,來自動更新信息等,這一點 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 在不改變算法的體系結(jié)構(gòu)的前提下得到重新定義算法內(nèi)的某些步驟。

            ?

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

            ?

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

            ?

            ??? 行為模式之 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 的時候擴展系統(tǒng)的功能,它不像 Template Method 需要 subclass 才能擴展新的功能。

            ?

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

            ?

            ??? 結(jié)構(gòu)模式之 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 的接口轉(zhuǎn)換為另外一種 clients 所期望的接口,使得這些 classes 可以更好地協(xié)同工作起來,而不至于因為別的不兼容的問題所影響。

            ?

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

            ?

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

            ?

            InputStreamReader isr = new InputStreamReader(

            new FileInputStream("test.txt"));

            ??? BufferedReader br = new BufferedReader(isr);

            ??? String line = br.readLine();

            ?

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

            ?

            ??? 結(jié)構(gòu)模式之 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. 翻譯為中文大致意思是:為一個子系統(tǒng)的一批接口提供一個統(tǒng)一標準的接口, Facade 定義更高層次的接口,使得子系統(tǒng)更容易使用。

            ?

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

            ?

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

            ?

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

            ?

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

            ?

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

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

            ?????? a) C++ Programming Language Bjarne Stroustrup

            ?????? b) C++ Primer Stanley B Lippman

            ?????? c) Effective C++ Scott Meyers

            ?????? d) Design Patterns GoF

            ??? 2, 提高或?qū)W習的書

            ?????? 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.shnenglu.com/javenstudio/ 20061228

            posted on 2006-12-31 10:31 Javen-Studio 閱讀(4418) 評論(5)  編輯 收藏 引用

            評論

            # re: 侯捷《C++/OOP/GP/DP》講座心得[未登錄] 2011-08-31 14:36 Chipset
            《 C++ Programming Language 》 Bjarne Stroustrup OK
            C++ Standard ISO/IEC14482 OK

            其他的都是垃圾。  回復  更多評論
              

            # re: 侯捷《C++/OOP/GP/DP》講座心得 2011-10-17 17:37 將盡快看看
            就看看iop,i80pop8366  回復  更多評論
              

            # re: 侯捷《C++/OOP/GP/DP》講座心得 2011-10-17 17:38 將盡快看看
            889088i90-p[opp【【p ii9003734  回復  更多評論
              

            # re: 侯捷《C++/OOP/GP/DP》講座心得 2013-05-07 10:16 dangerman
            看了這么多UML的文章都沒明白聚合和組合的區(qū)別,都是互相抄,看了您的文章終于明白了,謝謝  回復  更多評論
              

            # re: 侯捷《C++/OOP/GP/DP》講座心得 2013-05-07 11:17 溪流
            學習了  回復  更多評論
              

            亚洲人成电影网站久久| 国产成人精品久久一区二区三区av | 国产成人无码久久久精品一| 欧美va久久久噜噜噜久久| 99久久精品午夜一区二区| 热久久这里只有精品| 久久综合久久性久99毛片| 亚洲欧美成人综合久久久| 久久777国产线看观看精品| 理论片午午伦夜理片久久| 久久久久波多野结衣高潮| 久久精品视频网| 久久综合鬼色88久久精品综合自在自线噜噜 | 国产精品一区二区久久国产| 国产亚洲成人久久| 成人午夜精品无码区久久| 日韩亚洲欧美久久久www综合网| 欧美与黑人午夜性猛交久久久| 久久精品卫校国产小美女| 久久―日本道色综合久久| 国产免费久久精品99re丫y| 久久九九有精品国产23百花影院| 日日狠狠久久偷偷色综合0| 91精品国产91久久久久福利| 久久久久成人精品无码| 国产产无码乱码精品久久鸭| 亚洲国产精品嫩草影院久久 | 一本久久精品一区二区| 国产精品久久国产精麻豆99网站| 亚洲国产日韩欧美综合久久| 美女写真久久影院| 久久这里只有精品18| 无码任你躁久久久久久| 国产精品嫩草影院久久| 久久丫精品国产亚洲av| 亚洲乱码日产精品a级毛片久久| 国内精品久久久久影院免费| 亚洲AV无码1区2区久久| 精品久久久久久久久免费影院| 久久国产精品免费一区二区三区| 青草国产精品久久久久久|