青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-4  評論-40  文章-117  trackbacks-0

 
  (轉)

關于低耦合的消息傳遞,實現的方式有很多,哪種方法更好與具體的使用環境有關,本文使用試錯的方法,逐步探索達成這一目的具體方式,并理解實現方式背后的原因。

 

面向對象的系統當中,不可避免的有大量的類間消息傳遞的需求:一個類需要通知另一個或幾個類做些什么。

 

這種類間消息傳遞,簡單的說,就是調用其他類的方法。

如下:

void A::OnMessageXX()

{

         B::GetInstance()->DoSomething();

}

在這里,類A需要通知類B做些事情。這種調用在所有的面向對象程序中都是極其常見的。

 

但是如果類A需要調用類B,就不可避免的產生了耦合性。雖然耦合性終歸是不可能完全避免的,但是在一定程度上降低耦合性是完全可能的。

(至于為什么在設計中應該盡可能降低耦合性,不在本文的探討范圍之內)

 

上面的例子,我們使用了Singleton的模式,從全局作用域中獲取了B的實例,并調用了B的相關方法。使用Singleton的一個缺點是,假若我們希望對類A編寫測試代碼,我們需要做一些額外的解耦合工作。(關于編寫測試與解耦合,可以參考Robert C. Martin Series的Working Effectively with Legacy Code一書,該書的中譯版在這

我們也可以通過將B參數化的方法降低A與B間的耦合程度,像下面這樣:

void A::OnMessageXX(B* pBInstance)

{

         pBInstance->DoSomething();

}

現在的寫法要比之前的做法耦合性低,通過使用多態的方法,現在傳入函數的類B指針可能是另一個實現了B的相應接口的派生類,A并不關心B接口背后的具體實現。

 

但是等等,你說,現在對類B的耦合性雖然在A中被降低了,但是依舊存在于調用A::OnMessageXX的地方。在那里我們還是需要取得B的實例,然后傳遞給A。

 

沒錯,是這樣。

 

通過參數化類A的方法,我們把類A與類B間的耦合轉移了一部分到A的調用者那里。實際上總的耦合并沒有消除,只是被分解了。但是程序設計中不可能完全不存在耦合,我們需要做的是”正確”,而不是”完美”。類A的耦合性降低了,使得我們在未來需求變更的時候,類A有更大的可能性不需要被修改,并且對功能的擴展更加友好,這就達成了我們的目標了。

基于上述做法,如果我們在未來擴展是派生出一個B的子類,override相關的方法,那么類A的代碼基本是不需要修改的。

 

不過,問題是,假若A::OnMessageXX中,并不僅僅需要對類B發出消息,還需要對一系列相關的類B1,B2,B3等等發出消息呢?

 

哦,或許我們可以這樣做:

 

void A::OnMessageXX(const std::list<B*>& lstBInstances)

{

         for (std::list<B*>::const_iterator itr = lstBInstances.begin();

                   itr != lstBInstances.end();

                   ++itr)

         {

                   (*itr)->DoSomething();

}

}

 

是的,上面這是一種做法,有一系列B的對象需要被通知到,所以我們可以用一個列表把他們串起來,然后在循環中通知他們去干活。不過這樣做的前提是,這一系列B對象都是派生自一個公共基類B,有共通的接口;此外,我們需要在A的OnMessageXX被調用之前構造一個需要接受通知的B對象列表。

 

當A需要通知B,C,D等一系列沒有公共接口的對象的時候,上面的這種做法就無法處理了。

 

對于B、C、D等需要由A來調用的類來說,它們需要在A通知它們的時候,做一些特定的事情。而又A則是在某些特定的時刻需要通知B、C、D。這樣,我們可以把問題看成一個消息響應機制。

 

B、C、D可以在A的某些事件上注冊一些回調函數,當事件發生時,A確保注冊該事件的函數被調用到。

 

如下:

typedef void(callback*)();

class A {

public:

         enum EventIds {

         EVENT_MSG1,

         EVENT_MSG2,

};

void RegisterEvent(int nEventId, callback pfn);

private:

callback m_pfnCallback;

};

 

現在,B可以調用A::RegisterEvent注冊一個事件,并傳遞一個函數指針給A。

當A中發生了注冊的事件時,這個函數指針會被回調到。

不過這種簡單的做法適應性很差:

1、  不能支持單個事件的多個callback (可能有很多類都需要注冊該事件,并在事件發生時依次被回調)

2、  不能支持多個事件的同時存在

3、  回調函數沒有參數’

 

針對問題1,2,我們可以使用一個事件映射解決問題,做法如下:

typedef int EventId;

typedef void (callback*)();

typedef std::list<callback> CallbackList;

typedef std::map<EventId, CallbackList> CallbackMap;

現在這個數據結構就能夠支持多個event同時存在,且每個event都可以支持多個回調函數了。

 

但是這種用法依舊很不方便,如果類B想要注冊A上的一個事件,他需要定義一個 callback類型的函數,并把這個函數的地址傳遞給A。問題是,往往我們希望類B的回調函數在被調用到的時候,對類B中的數據和狀態進行修改,而一個單獨的函數,若想獲得/修改B中的狀態,則必須要與類B緊密耦合。(通過獲取全局對象,或者Singleton的方式)

這種緊密耦合引發我們的思考,能否在Callback中同時包含類B的指針與類B的成員函數。

 

答案是肯定的:泛型回調就可以做到這一點。關于泛型回調(Generic callback)的信息,在Herb Sutter的Exceptional C++ Style的35條中有詳細介紹。

 

一下比較簡單的泛型回調的定義如下:

class callbackbase {

public:

virtual void operator()() const {};

virtual ~callbackbase() = 0 {};

};

 

template <class T>

class callback : public callbackbase {

public:

typedef void (T::*Func)();

callback(T& t, Func func) : object(t), f(func) {}     // 綁定到實際對象

void operator() () const { (object->*f)(); }              // 調用回調函數

private:

T* object;

Func f;

};

 

有了這種泛型回調類,我們就可以將類B的實例與B的成員回調函數綁定在一起注冊到容器當中了,而不必再被如何在普通函數中修改B對象狀態的問題所困擾了。不過回調函數的參數問題依舊。如果想支持參數,我們不得不對每一種參數類型做一個不同的typedef,像上面定義的這樣 typedef void (T::*Func)();(如:typedef void (T::*Func)(int);)

一種解決方案是借助于Any(一種任意類型類)進行參數傳遞。

 

但是還有更完善的解決方案,不需要id號,也不需要泛型回調,Ogre采用Listener的方式實現的類間消息傳遞不僅可以支持單個類B對類A中某個事件的單次/多次注冊,也可以支持類B、C、D對同一個事件的注冊。而且可以完美的解決參數傳遞問題。

 

具體的方案如下:

class A {

public:

         class Listener {

         public:

                   virtual void OnMessageXX(int param1, float param2) = 0;

                   virtual void OnMessageYY(int param1, const std::string& param2) = 0;

};

 

void registerListener(Listener* obj) { m_lstListener.push_back(obj); }

void removeListener(Listener* obj)

{

         ListenerList::iterator itr = std::find(m_lstListener.begin(), m_lstListener.end(), obj);

         if (itr != m_lstListener.end())

                   m_lstListener.erase(itr);

}

 

private:

         typedef std::list<Listener*> ListenerList;

         ListenerList m_lstListeners;

};

 

有了以上定義,當類A收到某個消息XX之后,只需遍歷m_lstListeners列表,調用所有列表成員的OnMessageXX即可。

 

而所有注冊A的消息的類,都必須從A::Listener派生一個類,在它感興趣的消息處理函數中做出相應處理,而對不感興趣的消息,只需設為空函數即可。

 

一個簡單的類B的定義如下:

 

 

class B {

public:

         friend class BListener;

         class BListener : public A::Listener {

         public:

                   BListener(B* pBInstance) : m_pBInstance(pBInstance) {}

                   virtual void OnMessageXX(int param1, float param2)

{ m_pBInstance->DoSomething(); }

                   virtual void OnMessageYY(int param1, const std::string& param2) {}

         private:

                   B* m_pBInstance;

};

 

explicit B(A* pAInstance) : m_pAInstance(pAInstance)

{

m_pListener(new BListener(this));

m_pAInstance->registerListener(m_pListener);

}

         ~B() { m_pAInstance->removeListener(m_pListener); delete m_pListener; }

 

void DoSomething();

 

private:

         BListener* m_pListener;

}

 

類B在創建自身實例時,接受一個A的指針(這是合理的,因為類B需要監聽類A的消息,理應知道A的存在),并創建一個派生自A::Listener的監聽者對象,并把自身的指針傳遞給該對象,以使得該監聽者改變類B的狀態,而后類B將創建好的監聽者對象加入到A的監聽者列表中。

在B進行析構的時候,需要從A中刪除自己注冊的監聽者。而后將該對象釋放。

 

這種做法的好處:

1、  類B(以及類C等)對類A實現了信息隱藏,類A不再關注任何需要監聽它自身消息的其他類,只需關注其自身的狀態。從而減低了類A與其他與之關聯的類之間的耦合。(類A不必再費盡心機的去獲取B的指針,不管是通過全局變量,還是Singleton,還是參數,還是類成員變量,都不再需要了,A只關心在Listener中定義好的一組接口即可)而且,如果有必要類B可以對同一個消息注冊多次,且可以對同一消息有不同的反應(通過定義不同的BListener實現達到這一目的),只需在B不再需要監聽相關消息時將所注冊過的對象注銷掉即可。

2、  由于1中所述,類A的實現無需關心類B的實現,因此類A的邏輯中不需要包含任何類B的方法調用,從而,類A的cpp文件中,無需包含類B的頭文件,(可能還包括類C,D等等,此處類B指代需要根據類A狀態而做出動作的類)從而降低編譯時間,這是解耦合所帶來的附加好處。

3、  同樣是解耦合帶來的好處:因為無需關注類B等等其他類的實現,類A的代碼邏輯變得更加清晰,并且減少未來邏輯需求變更的改動所需要付出的代價(邏輯變更可能需要更改接口,需要增加狀態判斷,無論是調試時間還是編譯時間都是不可忽視的代價)。



posted on 2009-06-03 17:36 李陽 閱讀(466) 評論(0)  編輯 收藏 引用

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            精品成人一区二区三区| 欧美久久久久| 亚洲美女91| 国产精品国产一区二区| 午夜精品视频在线观看| 亚洲第一精品夜夜躁人人躁| 久久久亚洲国产美女国产盗摄| 欧美激情视频免费观看| 亚洲女人天堂av| 亚洲国产欧美一区二区三区同亚洲| 欧美国产综合视频| 亚洲一区视频在线观看视频| 欧美成人精精品一区二区频| 亚洲一区区二区| 91久久精品久久国产性色也91| 国产精品国产自产拍高清av王其 | 亚洲图片激情小说| 久久激情网站| 午夜精品国产| 亚洲精品视频免费| 国产一区二区三区四区五区美女| 欧美日韩不卡| 欧美极品aⅴ影院| 久久久精品一区| 一本一道久久综合狠狠老精东影业 | 国产精品久久久一本精品| 欧美区国产区| 国产亚洲福利| 夜夜爽www精品| 亚洲高清不卡一区| 国产午夜精品一区理论片飘花 | 亚洲精品在线看| 999亚洲国产精| 亚洲国产片色| 午夜欧美视频| 亚洲国产高清一区二区三区| 亚洲私人影院在线观看| 美女诱惑一区| 美女日韩欧美| 国产精品福利影院| 日韩小视频在线观看| 欧美一级视频一区二区| 欧美一区二区久久久| 午夜在线成人av| 亚洲国产精彩中文乱码av在线播放| 欧美在线观看你懂的| 91久久黄色| 一区二区三区免费观看| 免费视频亚洲| 国产精品激情电影| 亚洲国产一区二区在线| 玖玖玖免费嫩草在线影院一区| 久久久91精品国产| 免费人成网站在线观看欧美高清| 久久综合五月| 欧美一站二站| 欧美激情二区三区| 一区二区三区在线免费视频| 久久久成人精品| 午夜精品理论片| 免费观看一区| 亚洲二区在线| 欧美成人精品在线播放| 91久久精品一区二区别| 久久人体大胆视频| 欧美性猛交xxxx乱大交蜜桃| 狠狠色丁香久久婷婷综合_中| 永久域名在线精品| 亚洲一区二区三区视频播放| 久久久久久九九九九| 午夜一区二区三视频在线观看| 国产精品美女黄网| 久久国产日本精品| 久久久天天操| 91久久精品国产91久久性色tv| 91久久久久久国产精品| 欧美伦理一区二区| 香蕉免费一区二区三区在线观看 | 久久久久久久久久久久久9999 | 另类尿喷潮videofree| 欧美三级电影一区| 伊人久久大香线蕉av超碰演员| 久久九九国产精品| 久久午夜视频| 亚洲视频中文字幕| 亚洲电影免费观看高清| 亚洲欧洲日韩综合二区| 亚洲日产国产精品| 香蕉国产精品偷在线观看不卡| 国产一区二区三区的电影| 巨乳诱惑日韩免费av| 欧美国产综合一区二区| 亚洲欧美日韩天堂一区二区| 久久精品日产第一区二区| 日韩网站免费观看| 欧美影院午夜播放| 一区二区三区毛片| 亚洲国产专区校园欧美| 国产精品白丝av嫩草影院 | 久久久国产成人精品| 欧美福利一区二区| 久久精品72免费观看| 欧美激情视频一区二区三区在线播放| 亚洲图色在线| 看片网站欧美日韩| 香蕉成人伊视频在线观看| 免费人成网站在线观看欧美高清| 亚洲一二三区视频在线观看| 噜噜噜躁狠狠躁狠狠精品视频| 亚洲欧美激情视频| 欧美大片第1页| 亚洲欧洲日本国产| 亚洲一区二区三区色| 91久久综合亚洲鲁鲁五月天| 亚洲欧美精品在线| 国产午夜精品久久久久久免费视| 欧美福利一区二区三区| 国产精品久久久久一区二区三区共| 麻豆精品视频在线观看| 国产精品影片在线观看| 久久久亚洲国产天美传媒修理工| 亚洲一区二区三区四区视频| 日韩午夜精品视频| 久久手机精品视频| 久久久www| 国产一区二区三区高清在线观看| 一本久道久久综合中文字幕| 99国产精品久久久久久久成人热| 久久综合影视| 欧美高清视频在线播放| 亚洲电影在线| 亚洲精品乱码久久久久久黑人 | 99综合视频| 国产精品一区二区久久久久 | 亚欧成人在线| …久久精品99久久香蕉国产| 欧美亚洲免费| 久久精品成人一区二区三区蜜臀| 国产精品久久久久国产a级| 一本久久综合亚洲鲁鲁| 亚洲专区一区| 国产精品网站在线播放| 免播放器亚洲| 在线精品国精品国产尤物884a| 午夜一区二区三区在线观看| 久久精品国产亚洲精品 | 亚洲高清久久久| 亚洲福利视频免费观看| 久久免费视频观看| 久久综合一区| 亚洲国产一区二区视频| 欧美金8天国| 日韩视频中午一区| 国产精品久久久久影院亚瑟| 亚洲最快最全在线视频| 亚洲女女做受ⅹxx高潮| 国产精品专区h在线观看| 欧美亚洲尤物久久| 快she精品国产999| 亚洲精品日韩欧美| 国产精品久久久久久影院8一贰佰| 亚洲欧美制服中文字幕| 欧美成人a视频| 一区二区三区日韩欧美| 国产视频精品xxxx| 欧美成人午夜激情| 亚洲视频1区| 99热这里只有成人精品国产| 欧美三级在线视频| 欧美一区二区三区喷汁尤物| 欧美国产精品劲爆| 亚洲一区二区三区影院| 国产日韩欧美在线播放不卡| 久久亚洲视频| 国产精品99久久久久久久vr | 久久精品国产精品| 香蕉视频成人在线观看| 尤物九九久久国产精品的分类| 欧美激情一区二区| 午夜欧美大片免费观看| 欧美国产先锋| 欧美一二区视频| 99综合电影在线视频| 激情成人av在线| 国产精品露脸自拍| 欧美日本不卡高清| 久久青青草综合| 欧美亚洲一区在线| 亚洲一区中文| 亚洲黄色一区二区三区| 久久精品国产一区二区三区免费看| 日韩五码在线| 在线日韩成人| 国产一区二区黄色| 欧美性色视频在线| 欧美精品国产| 欧美成人伊人久久综合网| 久久精品一区蜜桃臀影院| 亚洲欧洲av一区二区| 一本一道久久综合狠狠老精东影业 |