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

每天早晨叫醒你的不是鬧鐘,而是夢(mèng)想

  C++博客 :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
  62 Posts :: 0 Stories :: 5 Comments :: 0 Trackbacks

常用鏈接

留言簿(1)

我參與的團(tuán)隊(duì)

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

關(guān)于低耦合的消息傳遞,實(shí)現(xiàn)的方式有很多,哪種方法更好與具體的使用環(huán)境有關(guān),本文使用試錯(cuò)的方法,逐步探索達(dá)成這一目的具體方式,并理解實(shí)現(xiàn)方式背后的原因。

面向?qū)ο蟮南到y(tǒng)當(dāng)中,不可避免的有大量的類間消息傳遞的需求:一個(gè)類需要通知另一個(gè)或幾個(gè)類做些什么。

這種類間消息傳遞,簡(jiǎn)單的說,就是調(diào)用其他類的方法。

如下:

1void A::OnMessageXX()
2{
3         B::GetInstance()->DoSomething();
4
5}

6
7

 

在這里,類A需要通知類B做些事情。這種調(diào)用在所有的面向?qū)ο蟪绦蛑卸际菢O其常見的。

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

(至于為什么在設(shè)計(jì)中應(yīng)該盡可能降低耦合性,不在本文的探討范圍之內(nèi))

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

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

1 void A::OnMessageXX(B* pBInstance)
2 {
3          pBInstance->DoSomething();
4 
5 }
6 
7 

 

現(xiàn)在的寫法要比之前的做法耦合性低,通過使用多態(tài)的方法,現(xiàn)在傳入函數(shù)的類B指針可能是另一個(gè)實(shí)現(xiàn)了B的相應(yīng)接口的派生類,A并不關(guān)心B接口背后的具體實(shí)現(xiàn)。

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

沒錯(cuò),是這樣。

通過參數(shù)化類A的方法,我們把類A與類B間的耦合轉(zhuǎn)移了一部分到A的調(diào)用者那里。實(shí)際上總的耦合并沒有消除,只是被分解了。但是程序設(shè)計(jì)中不可能完全不存在耦合,我們需要做的是”正確”,而不是”完美”。類A的耦合性降低了,使得我們?cè)谖磥硇枨笞兏臅r(shí)候,類A有更大的可能性不需要被修改,并且對(duì)功能的擴(kuò)展更加友好,這就達(dá)成了我們的目標(biāo)了。

基于上述做法,如果我們?cè)谖磥頂U(kuò)展是派生出一個(gè)B的子類,override相關(guān)的方法,那么類A的代碼基本是不需要修改的。

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

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

 

void A::OnMessageXX(const std::list<B*>& lstBInstances)
{
         
for (std::list<B*>::const_iterator itr = lstBInstances.begin();
                   itr 
!= lstBInstances.end();
                   
++itr)
         
{
                   (
*itr)->DoSomething();

         }

}



是的,上面這是一種做法,有一系列B的對(duì)象需要被通知到,所以我們可以用一個(gè)列表把他們串起來,然后在循環(huán)中通知他們?nèi)ジ苫睢2贿^這樣做的前提是,這一系列B對(duì)象都是派生自一個(gè)公共基類B,有共通的接口;此外,我們需要在A的OnMessageXX被調(diào)用之前構(gòu)造一個(gè)需要接受通知的B對(duì)象列表。

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

對(duì)于B、C、D等需要由A來調(diào)用的類來說,它們需要在A通知它們的時(shí)候,做一些特定的事情。而又A則是在某些特定的時(shí)刻需要通知B、C、D。這樣,我們可以把問題看成一個(gè)消息響應(yīng)機(jī)制。

B、C、D可以在A的某些事件上注冊(cè)一些回調(diào)函數(shù),當(dāng)事件發(fā)生時(shí),A確保注冊(cè)該事件的函數(shù)被調(diào)用到。

如下:

typedef void(callback*)();

class A {

public:

         enum EventIds {

         EVENT_MSG1,

         EVENT_MSG2,

};

void RegisterEvent(int nEventId, callback pfn);

private:

callback m_pfnCallback;

};

現(xiàn)在,B可以調(diào)用A::RegisterEvent注冊(cè)一個(gè)事件,并傳遞一個(gè)函數(shù)指針給A。

當(dāng)A中發(fā)生了注冊(cè)的事件時(shí),這個(gè)函數(shù)指針會(huì)被回調(diào)到。

不過這種簡(jiǎn)單的做法適應(yīng)性很差:

1、  不能支持單個(gè)事件的多個(gè)callback (可能有很多類都需要注冊(cè)該事件,并在事件發(fā)生時(shí)依次被回調(diào))

2、  不能支持多個(gè)事件的同時(shí)存在

3、  回調(diào)函數(shù)沒有參數(shù)’

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

typedef int EventId;

typedef void (callback*)();

typedef std::list<callback> CallbackList;

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

現(xiàn)在這個(gè)數(shù)據(jù)結(jié)構(gòu)就能夠支持多個(gè)event同時(shí)存在,且每個(gè)event都可以支持多個(gè)回調(diào)函數(shù)了。

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

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

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

一下比較簡(jiǎn)單的泛型回調(diào)的定義如下:

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) {}     // 綁定到實(shí)際對(duì)象

void operator() () const { (object->*f)(); }              // 調(diào)用回調(diào)函數(shù)

private:

T* object;

Func f;

};

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

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

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

具體的方案如下:

 1class A {
 2public:
 3         class Listener 
 4        {
 5           public:
 6
 7                   virtual void OnMessageXX(int param1, float param2) = 0;
 8
 9                   virtual void OnMessageYY(int param1, const std::string& param2) = 0;
10
11        }
;
12
13void registerListener(Listener* obj) 
14
15   m_lstListener.push_back(obj); 
16}

17
18void removeListener(Listener* obj)
19{
20         ListenerList::iterator itr = std::find(m_lstListener.begin(), m_lstListener.end(), obj); 
21
22         if (itr != m_lstListener.end())
23                   m_lstListener.erase(itr);
24}

25
26private:
27         typedef std::list<Listener*> ListenerList;
28
29         ListenerList m_lstListeners;
30}
;
31
32

 

有了以上定義,當(dāng)類A收到某個(gè)消息XX之后,只需遍歷m_lstListeners列表,調(diào)用所有列表成員的OnMessageXX即可。

而所有注冊(cè)A的消息的類,都必須從A::Listener派生一個(gè)類,在它感興趣的消息處理函數(shù)中做出相應(yīng)處理,而對(duì)不感興趣的消息,只需設(shè)為空函數(shù)即可。

一個(gè)簡(jiǎn)單的類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在創(chuàng)建自身實(shí)例時(shí),接受一個(gè)A的指針(這是合理的,因?yàn)轭怋需要監(jiān)聽類A的消息,理應(yīng)知道A的存在),并創(chuàng)建一個(gè)派生自A::Listener 的監(jiān)聽者對(duì)象,并把自身的指針傳遞給該對(duì)象,以使得該監(jiān)聽者改變類B的狀態(tài),而后類B將創(chuàng)建好的監(jiān)聽者對(duì)象加入到A的監(jiān)聽者列表中。

在B進(jìn)行析構(gòu)的時(shí)候,需要從A中刪除自己注冊(cè)的監(jiān)聽者。而后將該對(duì)象釋放。

這種做法的好處:

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

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

3、  同樣是解耦合帶來的好處:因?yàn)闊o需關(guān)注類B等等其他類的實(shí)現(xiàn),類A的代碼邏輯變得更加清晰,并且減少未來邏輯需求變更的改動(dòng)所需要付出的代價(jià)(邏輯變更可能需要更改接口,需要增加狀態(tài)判斷,無論是調(diào)試時(shí)間還是編譯時(shí)間都是不可忽視的代價(jià))。

 

本文來自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/zougangx/archive/2009/07/30/4395775.aspx

posted on 2011-05-12 18:12 沛沛 閱讀(398) 評(píng)論(0)  編輯 收藏 引用 所屬分類: C++
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲国产精品一区二区尤物区| 中日韩午夜理伦电影免费| 日韩视频在线免费| 亚洲伦理网站| 亚洲视频专区在线| 亚欧美中日韩视频| 久久综合九色欧美综合狠狠| 欧美丰满少妇xxxbbb| 亚洲精品欧美专区| 亚洲——在线| 免费观看一区| 国产乱码精品一区二区三| 1769国产精品| 亚洲综合色自拍一区| 蜜桃久久av一区| 一本大道久久a久久综合婷婷 | 久久中文字幕一区| 欧美日韩国产黄| 国内精品伊人久久久久av影院 | 国产精品一区免费观看| 国产综合色一区二区三区| 日韩一本二本av| 久久久久88色偷偷免费| 亚洲理论在线观看| 免费成人av在线| 国产无遮挡一区二区三区毛片日本| 亚洲国产你懂的| 欧美在线观看视频在线| 亚洲精品中文字幕在线| 久久综合国产精品| 国产一区二区中文| 亚洲欧美清纯在线制服| 亚洲国产一二三| 欧美在线看片a免费观看| 欧美日韩精品一区二区| 在线观看欧美亚洲| 久久久久久久久岛国免费| 中日韩男男gay无套| 欧美精品免费在线观看| 狠狠色狠狠色综合日日tαg| 亚洲欧美日韩在线观看a三区| 亚洲日韩中文字幕在线播放| 米奇777在线欧美播放| 狠狠色狠狠色综合| 欧美专区在线| 欧美一区二区三区日韩视频| 日韩一级网站| 欧美成人乱码一区二区三区| 在线观看亚洲一区| 巨乳诱惑日韩免费av| 性欧美1819sex性高清| 国产精品久久久久aaaa| 亚洲免费在线播放| 亚洲影院在线| 国产精品一区二区在线观看| 性欧美video另类hd性玩具| 亚洲视频网在线直播| 国产精品sm| 午夜久久影院| 午夜亚洲视频| 国一区二区在线观看| 久久视频一区二区| 久久久久久亚洲综合影院红桃| 国外成人网址| 欧美国产精品va在线观看| 欧美寡妇偷汉性猛交| 一本色道久久综合狠狠躁篇怎么玩 | 美脚丝袜一区二区三区在线观看 | 亚洲欧美日韩区| 亚洲制服av| 黑人一区二区| 亚洲二区在线观看| 欧美精品v国产精品v日韩精品| 制服诱惑一区二区| 午夜影院日韩| 1769国内精品视频在线播放| 亚洲精品乱码久久久久久日本蜜臀 | 中日韩高清电影网| 亚洲在线网站| 在线观看日韩av先锋影音电影院| 亚洲国产精品一区二区久 | 亚洲美女精品成人在线视频| 亚洲精品久久久久久久久久久久 | 性亚洲最疯狂xxxx高清| 在线精品一区| 99精品视频免费全部在线| 国产免费成人在线视频| 免费观看久久久4p| 欧美日韩免费观看一区| 久久精品国产一区二区电影| 欧美a级片网| 久久精品人人做人人爽电影蜜月| 欧美久久在线| 老妇喷水一区二区三区| 99热这里只有精品8| 国产亚洲精品自拍| 亚洲精品一区中文| 一色屋精品视频免费看| 日韩午夜激情| 在线看片成人| 亚洲自拍电影| 99这里只有久久精品视频| 欧美中文字幕在线观看| 亚洲伊人伊色伊影伊综合网| 免费观看国产成人| 久久亚洲美女| 国产亚洲精品aa| 一区二区三区免费在线观看| 亚洲国产一区二区三区a毛片 | 久久久久88色偷偷免费| 欧美日韩一区二区三| 欧美激情女人20p| 激情五月综合色婷婷一区二区| 亚洲午夜性刺激影院| 一本色道久久综合狠狠躁篇怎么玩 | 小嫩嫩精品导航| 亚洲一级片在线观看| 欧美大尺度在线| 欧美 日韩 国产在线| 国产一区二区精品久久| 亚洲免费在线精品一区| 亚洲香蕉在线观看| 欧美日韩免费一区二区三区视频| 欧美国产在线电影| 在线精品视频一区二区三四| 久久久久九九视频| 久热爱精品视频线路一| 国色天香一区二区| 久久精品视频网| 久久久久久久国产| 国内自拍一区| 久久麻豆一区二区| 免费成人小视频| 亚洲第一黄色网| 欧美a级一区二区| 亚洲高清激情| 一区二区不卡在线视频 午夜欧美不卡'| 久久亚裔精品欧美| 欧美激情第4页| 一本综合精品| 国产精品视频免费在线观看| 午夜精品久久久久久久 | 亚洲欧美成人一区二区在线电影| 亚洲欧美电影在线观看| 国产伦精品一区| 欧美中文字幕在线播放| 免费一区二区三区| 最新国产成人在线观看| 欧美激情视频一区二区三区免费| 亚洲免费成人av| 欧美一区二区三区婷婷月色| 国产一区二区三区四区三区四| 欧美成人首页| 在线一区观看| 国产主播精品| 欧美成人综合一区| 一区二区三区偷拍| 久久久亚洲国产美女国产盗摄| 亚洲国产精品一区制服丝袜 | 欧美日韩一区二区在线观看视频| 亚洲深爱激情| 久久午夜激情| 一级成人国产| 黄网站色欧美视频| 欧美人与性动交a欧美精品| 亚洲线精品一区二区三区八戒| 久久久精品国产99久久精品芒果| 亚洲国产片色| 欧美色图天堂网| 久久久久**毛片大全| 日韩视频在线播放| 可以免费看不卡的av网站| 亚洲特级毛片| 亚洲成人在线视频网站| 国产精品视频午夜| 欧美大片免费观看| 亚洲欧美在线另类| 99av国产精品欲麻豆| 蜜臀av性久久久久蜜臀aⅴ| 亚洲主播在线观看| 亚洲区一区二区三区| 国产亚洲欧美日韩美女| 欧美少妇一区| 欧美国产一区二区在线观看 | 香蕉av福利精品导航| 最新中文字幕亚洲| 久久综合狠狠综合久久综青草| 一区二区三区四区五区视频 | 久久精品国产99| 亚洲私人影院| 亚洲黄一区二区| 麻豆成人精品| 久久www成人_看片免费不卡| 在线亚洲高清视频| 亚洲精品日韩久久| 亚洲二区在线观看| 韩国自拍一区| 国产一区二区精品久久99| 国产精品视频专区| 国产精品久久久久aaaa九色|