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

Focus on ACE

訂閱 ace-china
電子郵件:
瀏覽存于 groups.google.com 上的所有帖子

C++博客 首頁 新隨筆 聯系 聚合 管理
  64 Posts :: 3 Stories :: 22 Comments :: 0 Trackbacks

避免依賴的消息處理方式

Anthony Williams
url: http://www.ddj.com/dept/cpp/184429055
譯者: Stone Jiang
譯者說明:本人還在學習英文的過程中,有些句子很難譯,這里給出原文的鏈接,歡迎就其中譯得不準確的地方與我交換意見。

在您維護安全類型和避免集成電路般函數時,你可以使用C++的強大的力量進行消息傳遞。

Anthony是Just Software Solution有限公司的一位軟件開發者和執行管理者??梢酝ㄟ^anthony@justsoftwaresolutions.co.uk與之聯系。

使用通用的消息傳遞方式傳遞數據在C++程序中很普遍。這種技術經常用于在線程間以及從/到GUI組件間傳遞數據。但是消息傳遞仍然很難實現得良好,這是因為在常見的消息傳遞方式中,暴露出了過多的藕合、缺少類型安全和集成電路般的消息處理函數。

在本文中,我提出了一種技術,這種技術利用C++的強大力量來避免上述缺陷——在消息傳遞中避免不適當的藕合,維護類型安全,以及消除集成電路般的消息處理函。( The only translation units that need to known the details of a message are those containning the source and handler functions for that specific message type.) 需要轉換的單元,即需要知道的消息詳細內容是包含了特定消息的類型的源代碼和處理函數。

傳統技術


大概應用得最為廣泛的消息傳遞技術是使用一個帶有特殊成員來表示消息類型的結構體,該消息類型是消息的標識。這種方式被廣泛應用歸咎于使用了基于C的API,比如X11和Microsoft Windows。在這種方法中,消息結構體中要么有一個通用的字體用于區別不同消息的意義,這個字段可被所有消息重用,或者它是更大結構的第一個成員,它的類型由類型代碼來確定。Windows API使用前面的技術,而X11使用后面的方法。無論用哪種方式,處理消息的代碼都須檢查類型編碼,用以決定怎么處理該消息。

這些技術的問題是:缺乏類型安全,集成電路般的處理函數,需要管理類型編碼來確保消息唯一性的適當層次。特別的,缺乏類型安全意味著使用之前,使用代碼必須把消息數據轉換成適當的類型。這一步是極易出錯的,尤其在當復制和粘貼代碼時(這種非常的手段常發生在為處理相似消息編寫代碼的時候),編譯器不會在這種錯誤給出任何警告。

缺乏類型安全還有一個額外的問題——即它不可能簡單有效的通過消息系統傳遞資源或變長的數據, 這是因為消息的發送方總是不能知道何時(或是否)該消息已被處理過了。

在這部分,集成電路般的消息處理函數是必須用于確定消息類型的產物,通過已接收的消息來消息類型,然后得到如何處理它的方式。這種處理函數往往實現為一個很大的switch語句或是一串if eles if。一些框架,如MFC,提供一些宏來減弱這種問題的影響,它這不能完全消除這個問題。

最后的問題是管理類型代碼。它必須要求接收消息代碼清楚地知道是哪一個消息,以便于正確的處理它。所以,類型代碼需要在處理它的相關代碼中確保唯一性。比如,在Windows API中,指定范圍的消息類型在不同的應用程序中代表不同的意義,并且,在同一個應就用程序中,其它范圍的消息類型在不同窗口或GUI組件中代表不同的意義。 通常,需要所有類型代碼的列表,該列表要求在給定的范圍中保持唯一,以便于檢查它們的唯一性。列表常常是以頭文件的形式給出,頭文件中定義了類型代碼,包含在需要知道消息類型的所有地方。這種方式容易導致應用程序不同部分之間的藕合,而這些部分之間卻沒有任何關系。由于這種過度的藕,簡單的變更導致過多的重新編譯。

面向對象技術

對象技術的一個常見特征是所有相關消息類派生自一個通用的基類。該特征用編譯器能認識的真實類型代替了顯式的類型代碼。不僅如此,它還有了一個重要的,超越C風格技術的優點——類型安全。它提供的通用基類的析構函數是虛函數,所以派生的消息類能自由地管理資源,如變長的數據,這些數據可以在析構函數中釋放。僅有的需求是接受消息的代碼能正確地銷毀消息對象,無論它們是否被處理。

管理類型代碼現在被替換為管理類。這是一個更加簡單的任務,由于可能的消息名字的范圍是沒有限制的,可能存在名字沖突,但這一點可以通過名字空間來解決。

保持簡單

最簡單的OOP技術就是用dynamic_cast檢查實際的消息類型代替檢查消息編碼。然而,這依然面臨著集成電路般地消息處理方式——現在通過包括dynamic_cast的比較鏈也優于通過類型編碼字段比較鏈。如列表1:

void ?handleMessage(Message * ?message)
{
????
if (Message1 * ?m = dynamic_cast < Message1 *> (message))
????
{
????????handleMessage1(m);
????}

????
else ? if (Message2 * ?m = dynamic_cast < Message2 *> (message))
????
{
????????handleMessage2(m);
????}

????
// ?
}

[列表1]

一般而言,由于僅僅是消息的源代碼和接受消息的源代碼需求知道相關的消息,所以依賴得到降低。然后,集成電路般地處理函數現在需要知道消息的有關細節,所以dynamic_cast需要消息的完整定義——如果分派給另外的函數處理實際的消息,C風格技術的處理函數不需求知道消息的細節。

雙重分派

(Direct testing of a class's type using dynamic_cast is generally indicative of a design problem;)類的類型用dynamic_cast的直測試一般可表示為設計問題;然而,簡單地把虛函數放在消息類中起不到任何作用——它將把消息處理與消息纏繞在一起,這個消息使在第一個地方發送消息的目的失敗。

雙重分派的關鍵點是,在消息類中的虛函數帶有一個作為參數的處理器,然后在處理器上把自已作為參數傳遞傳遞給另一個函數并完成調用。因為這里的第二次到處理器的回調已經在實際的派生類中完成,所以真實的消息類型已經知道,在處理器上能調用適當的函數,無論這個函數是通過重載的方式實現還是另外獨立命名的函數來實現(列表2)。

class ?Message
{
public :
????
virtual ? void ?dispatch(MessageHandler * ?handler) = 0 ;
};
class ?Message1:
????
public ?Message
{
????
void ?dispatch(MessageHandler * ?handler)
????{
????????handler
-> process( this );
????}
};
class ?Message2:
????
public ?Message
{
????
void ?dispatch(MessageHandler * ?handler)
????{
????????handler
-> process( this );
????}
};
// ?other?message?classes
class ?MessageHandler
{
????
void ?process(Message1 * );
????
void ?process(Message2 * );
????
// ?overloads?of?process?for?other?messages
};

[列表2]

依賴于重載的方式來區別不同的消息有利于大多數平衡——現在在每個消息類中虛函數的實現方式是相同的,如果需要,可以通過宏來一致地包裝,或通過從一個消息到另一個消息中直接復制,不會有出錯的機會。

雙重分派存在一個缺點——高度藕合。由于通過重載方式在處理器類中的選擇處理函數,在消息類中虛函數的實現需要知道處理器類的定義的全部,因此必須注意到在系統中每個其它的類的名字。不光這些,如果要支持不同的處理器類,處理函數必須在通用的處理器的基類中聲明為虛函數,所以每個處理器類必須在系統中注意到所有的消息類型(列表3)。增加或刪除一個消息類型會引起應用程序大部分代碼重新編譯。

class ?MessageHandler
{
????
virtual ? void ?process(Message1 * ) = 0 ;
????
virtual ? void ?process(Message2 * ) = 0 ;
????
virtual ? void ?process(Message3 * ) = 0 ;
????
virtual ? void ?process(Message4 * ) = 0 ;
????
// ?overloads?of?process?for?other?messages
}
;
class ?SpecificMessageHandler:
????
public ?MessageHandler
{
????
void ?process(Message1 * );
????
void ?process(Message2 * );
????
void ?process(Message3 * );
????
void ?process(Message4 * );
????
// ?overloads?of?process?for?other?messages
}
;
class ?OtherSpecificMessageHandler:
????
public ?MessageHandler
{
????
void ?process(Message1 * );
????
void ?process(Message2 * );
????
void ?process(Message3 * );
????
void ?process(Message4 * );
????
// ?overloads?of?process?for?other?messages
}
;

[列表3]

動態雙重分派

(It was against this backdrop that I developed the technique I call "Dynamic Double Dispatch.")我開發了一種技術,我稱其為“動態雙重分派”,這種技術用于解決上述問題。盡管有基本的雙重分派技術,但選擇的消息處理函數使用的是在編譯階段確定的重載技術(盡管發現在正確的消息處理器類中的實現是使用虛函數機制),而動態雙重分派是在運行時檢查在處理器上適當的處理函數的。結論是動態雙重分派消除了雙重分派的依賴問題。消息類型不在需要注意到其它的消息類型,并且處理器類僅需要注意到它的它要處理的消息。

動態檢查的關鍵點是:每一個消息類型有一個獨立的基類——處理器類從適當的,設計為處理消息的基類派生。然后在每個消息類中的分派函數能用dynamic_cast來檢查從正派基類派生的處理器類,因而實現了正確的處理函數。(列表4)

class ?MessageHandlerBase
{};
class ?Message1HandlerBase:
????
public ? virtual ?MessageHandlerBase
{
????
virtual ? void ?process(Message1 * ) = 0 ;
};
class ?Message1
{
????
void ?dispatch(MessageHandlerBase * ?handler)
????{
????????dynamic_cast
< Message1HandlerBase &> ( * handler).process( this );
????}
};
class ?Message2HandlerBase:
????
public ? virtual ?MessageHandlerBase
{
????
virtual ? void ?process(Message2 * ) = 0 ;
};
class ?Message2:
????
public ?MessageBase
{
????
void ?dispatch(MessageHandlerBase * ?handler)
????{
????????dynamic_cast
< Message2HandlerBase &> ( * handler).process( this );
????}
};
// ?
class ?SpecificMessageHandler:
????
public ?Message1HandlerBase,
????
public ?Message2HandlerBase
{
????
void ?process(Message1 * );
????
void ?process(Message2 * );
};
class ?OtherSpecificMessageHandler:
????
public ?Message3HandlerBase,
????
public ?Message4HandlerBase
{
????
void ?process(Message3 * );
????
void ?process(Message4 * );
};

[列表4]

(Of course, having a completely separate handler base class for each message type would add excessive complication, as the dispatch function for each message type would now be specific to that message type, and the base classes would have to be written separately, despite being fundamentally the same, except for the message type they referenced.)
誠然,為每個消息類型分別編寫的處理器基類將增加過多的復雜性,同樣地,每個消息類型各自的分派函數現在需要特別指定,基類也需求分別編寫,然后除了它們引用的消息類型外基礎是相同的。消除這種重復的關鍵是使基類成為模板,用消息類型作為模板參數——分派函數引用到模板的實現好于指定類型;請看列表5。

?

template < typename?MessageType >
class ?MessageHandler:
????
public ? virtual ?MessageHandlerBase
{
????
virtual ? void ?process(MessageType * ) = 0 ;
};
class ?Message1
{
????
void ?dispatch(MessageHandlerBase * ?handler)
????{
????????dynamic_cast
< MessageHandler < Message1 >&> ( * handler).process( this );
????}
};
class ?SpecificMessageHandler:
????
public ?MessageHandler < Message1 > ,
????
public ?MessageHandler < Message2 >
{
????
void ?process(Message1 * );
????
void ?process(Message2 * );
};

[列表5]
出于簡化原因,在消息類中的分派函數幾乎相同,但也不是完全相同——它們必須明確的指定屬于它們的指定消息類,以便于轉換為適當的處理器基類。像軟件中許多事情一樣,這個問題可以增加一個額外的層來解決——分派函數可以委托給單個模板函數,這個模板函數使用模板參數類型來確定消息類型和把處理器轉換到適當的類型上。(列表6)

?

class ?Message
{
protected :
????template
< typename?MessageType >
????
void ?dynamicDispatch(MessageHandlerBase * ?handler,MessageType * ?self)
????{
????????dynamic_cast
< MessageHandler < MessageType >&> ( * handler).process(self);
????}
};
class ?Message1:
????
public ?MessageBase
{
????
void ?dispatch(MessageHandlerBase * ?handler)
????{
????????dynamicDispatch(handler,
this );
????}
};

[列表6]

通過進一步抽象在消息對象中分派函數的不同之處,我們把工作集中到一個地方——模板函數的定義;它提供了為修改行為的單一點。在消息類中剩下的分派函數都是相同的,這足以把它們簡化到隱藏細節的宏中或在消息類之間中逐字復制。



未處理的消息

迄今為止,我們展示的 dynamicDispach模板函數的代碼假定處理的類是從適當的SpecificMessageHandler是派生的;如是不是這樣, dynamic_cast將拋出std::bad_cast異常。有時這就足夠了,但是有的時候,有更適當的行為——也許更好的做法是拋棄消息,這不能被接受消息的代理處理或調用catch-all處理器。舉例來說,dynamicDispatch 函數能被調整,用基于指針的轉換代替基于引用的轉換,所以結果值可以與NULL進行測試。


缺點(Trade-Off)在哪里?
有如此多的優點,一定存在它的缺點,那它的缺點在哪里呢?在這里,有兩個缺點。第一個是:額外的動態轉換,兩個虛函數調用會影響性能。如果性能上是一個問題,這就是一個疑問,但是,在很多情況下,花銷在這里的額外的時間是不值得關注的。可以使用相應的工具來簽定到底哪里才是真正的性能瓶頸所在。

第二個缺點是:需要為每個消息處理從指定的基類派生消息處理器。因為處理新的消息類型需要修改兩個地方——適當的基類列表入口和處理函數,所以這可能成為錯誤的來源,遺失處理函數容易被發現,因為這是全局點,但是遺失基類在代碼運行時只產生不易查覺的缺陷。因為沒有處理函數的時候僅僅是不調用它。這些錯誤在單元測試的時候是很容易被抓出來的,所以所實話,這些不便之處都成不了大問題。

?

posted on 2006-05-04 20:52 Stone Jiang 閱讀(1447) 評論(0)  編輯 收藏 引用 所屬分類: C++&OOP 、Miscellaneous
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            国产精品久久久久9999吃药| 亚洲午夜精品在线| 宅男噜噜噜66国产日韩在线观看| 黑人巨大精品欧美一区二区小视频| 亚洲日本中文字幕| 在线成人www免费观看视频| 亚洲午夜视频在线| 一区二区三区视频在线| 欧美阿v一级看视频| 久久视频在线免费观看| 国产精品一区免费在线观看| 亚洲精品一区二| 亚洲二区免费| 欧美在线91| 欧美一区免费视频| 欧美视频在线看| 亚洲精品免费观看| 亚洲蜜桃精久久久久久久| 久久久久久久久久久成人| 欧美一区二区大片| 国产精品久久久久久久久久尿| 亚洲片区在线| 在线亚洲一区二区| 欧美日韩国产91| 亚洲精品一区二区三区蜜桃久 | 欧美日韩成人在线| 亚洲欧洲日本国产| 日韩写真在线| 欧美久久久久| 99日韩精品| 亚洲欧美日韩综合| 国产精品自拍视频| 性色av一区二区三区红粉影视| 久久er精品视频| 国内精品写真在线观看| 欧美在线一区二区| 美女精品国产| 亚洲高清色综合| 欧美精品一区二区三区在线看午夜 | 一区二区三区四区五区在线| 欧美经典一区二区| 一本大道久久精品懂色aⅴ| 亚洲一区二区三区精品在线| 国产精品女主播| 欧美影院在线| 欧美激情2020午夜免费观看| 日韩亚洲欧美在线观看| 国产精品草草| 欧美一区午夜精品| 亚洲大片免费看| 亚洲一区二区在| 国产亚洲欧美日韩日本| 久久偷窥视频| 9人人澡人人爽人人精品| 欧美怡红院视频一区二区三区| 国产一区二区三区四区三区四| 猫咪成人在线观看| 国产精品99久久久久久久vr| 久久全国免费视频| 日韩一区二区精品| 国产一区二区三区在线观看精品| 久久综合五月| 亚洲午夜激情在线| 欧美激情bt| 欧美亚洲一区在线| 亚洲日韩第九十九页| 国产精品资源| 欧美国产日本在线| 欧美在线一二三区| 亚洲精品日韩激情在线电影| 久久精品中文| 亚洲一区二区高清| 亚洲国产精品成人va在线观看| 欧美日韩在线亚洲一区蜜芽| 久久久国产午夜精品| 99在线精品视频| 蜜臀久久99精品久久久久久9| 亚洲图片在线观看| 亚洲国内精品在线| 国产一区二区三区四区hd| 欧美巨乳波霸| 久久婷婷国产综合精品青草| 亚洲在线视频观看| 日韩西西人体444www| 欧美国产日韩一区| 久久亚洲欧美| 久久福利精品| 亚洲免费在线精品一区| 亚洲精品女av网站| 亚洲第一区中文99精品| 国产日韩一区二区三区在线播放| 欧美日韩一本到| 欧美激情亚洲自拍| 美女国产精品| 久久伊人免费视频| 久久精品国产免费看久久精品 | 亚洲激情在线| 免费在线看一区| 久久一区中文字幕| 久久久久久久综合日本| 欧美一区成人| 亚洲一线二线三线久久久| 99re热精品| 亚洲美女中文字幕| 日韩视频永久免费| 91久久精品一区二区别| 亚洲高清一区二区三区| 精品成人国产| 精品51国产黑色丝袜高跟鞋| 国产三区精品| 国产日韩欧美在线播放不卡| 国产区日韩欧美| 国产日韩欧美在线视频观看| 国产日产高清欧美一区二区三区| 国产精品乱码人人做人人爱| 国产精品福利网| 国产美女诱惑一区二区| 国产欧美在线视频| 激情六月婷婷久久| 亚洲福利在线视频| 日韩视频在线永久播放| 一本色道久久88精品综合| 在线一区二区日韩| 亚洲欧美韩国| 久久精品国产v日韩v亚洲| 久久久久一区| 欧美激情视频一区二区三区免费 | 9人人澡人人爽人人精品| 一区二区三区高清不卡| 一区二区三区四区国产| 亚洲欧美成人网| 久久久亚洲国产美女国产盗摄| 狼狼综合久久久久综合网| 欧美精品在线视频| 国产乱码精品一区二区三区五月婷| 国产一区二区三区在线观看精品 | 亚洲国产精品久久91精品| 亚洲精品国偷自产在线99热| 亚洲视频精品在线| 久久久久九九九| 欧美日韩国产免费| 国产欧美日韩一区二区三区在线观看 | 亚洲婷婷综合久久一本伊一区| 欧美一区二区三区免费看| 欧美www在线| 中文精品一区二区三区| 久久精品国产2020观看福利| 欧美精品日韩精品| 国产亚洲精品v| 亚洲精品欧美一区二区三区| 亚欧成人精品| 91久久国产综合久久| 午夜日韩av| 欧美日韩国产黄| 黄色一区二区在线| 亚洲综合另类| 欧美高清在线一区| 亚洲欧洲av一区二区三区久久| 蜜桃久久av| 国产一区二区三区在线观看视频| 日韩一区二区精品| 女生裸体视频一区二区三区| 在线一区二区三区四区五区| 你懂的视频欧美| 国产午夜一区二区三区| 中文亚洲免费| 亚洲高清一区二区三区| 欧美一区二区三区播放老司机| 欧美日韩不卡视频| 亚洲黄页视频免费观看| 久久久人成影片一区二区三区| 99综合在线| 欧美激情一区二区三区在线视频| 国产有码一区二区| 午夜精品久久久久久久久久久久久| 亚洲国产精品va在线观看黑人| 性做久久久久久免费观看欧美| 欧美午夜视频一区二区| 亚洲毛片av在线| 欧美高清视频www夜色资源网| 欧美一区二区视频在线观看2020 | 亚洲国产欧美一区二区三区同亚洲| 香蕉成人久久| 国产精品一区二区久久国产| 亚洲午夜久久久久久久久电影网| 亚洲激情国产精品| 欧美成人蜜桃| 亚洲精品看片| 亚洲高清一二三区| 欧美91大片| 亚洲精品国产品国语在线app| 美女主播精品视频一二三四| 久久精品在线免费观看| 国产一区二区丝袜高跟鞋图片 | 日韩视频在线观看免费| 欧美激情国产日韩精品一区18| 亚洲精品免费一区二区三区| 欧美黄色影院| 欧美极品影院| 亚洲视频在线看|