訊息耦合還是介面耦合

萬個函式千個類發表於2013-02-28


最近公司準備開發一個新產品,需要重新設計一套新的框架,但是就這框架中各模組的通訊方式,大家產生了爭論,主要集中在各模組的互動方式是訊息耦合還是介面耦合。

需求大概這樣,我們需要封裝一套客戶端SDK, 暴露一系列API給外部用,而這套SDK內部會有很多模組組成,這些模組之間相互會有互動。

第一種設計是基於介面耦合,框架如下:


這種介面方式的設計要點是:
a. 各模組以類似COM元件的方式封裝和暴露介面,也就是說模組會以介面的形式暴露介面,並且以Sink的方式通知外部事件。比如模組A的介面如下
class IA
{
public:
    virtual void fun1() = 0;
    virtual void fun2() = 0;
    .
    virtual void int AdviseSink(IASink* pSink) = 0;
    virtual void bool UnAdviseSink(int nCooki) = 0;
}

class IASink
{
public:
    virtual void Event1() = 0;
    virtual void Event2() = 0;
    .
}
b. 有一個統一的模組管理平臺來管理所有模組,可以通過該平臺查詢和載入需要的模組,然後後得到相應的介面指標進行操作。
c. 各模組間的互動全都通過直接呼叫其他模組的介面或是訂閱該模組的Sink來實現。

第二種設計是基於訊息耦合,框架如下:

這種訊息方式的設計要點是:
a. 各個模組只有一個訊息處理介面。 
    class IMessageHandler
    {
    public:
        virtual void ProcessMessage(Message& msg) = 0;
    };
b. 中間的訊息匯流排提供訊息的訂閱和分發,各個模組會向訊息匯流排註冊自己感興趣的訊息。
c. 需要呼叫某個模組介面 或是 觸發某個事件時,都是通過向訊息匯流排傳送訊息的方式, 然後由訂閱訊息的模組執行。

上面2種架構的設計方式各有優劣,下面我們來簡單比較一下:
(1)耦合性: 儘管基於介面的耦合已經降低了耦合性,但是相比訊息來說,顯然是訊息方式耦合性更弱。
(2)可擴充套件性: 某個模組新增加一個介面, 介面方式需要新加介面函式,而訊息方式只需要新加一個訊息型別。即使新增加一個模組,訊息方式只是新增加幾個訊息處理型別,非常方便。所以可擴充套件性來說,顯然也是訊息方式佔優。
(3)效能: 介面方式是直接呼叫,可是訊息方式需要經過訊息匯流排過濾分發, 顯然效能上介面方式更高。 
(4)編碼安全性,介面方式是強型別,介面一修改,編譯時就能很快發現問題;訊息方式卻是弱型別,訊息修改後,有可能要到執行時才能發現問題, 另外很多訊息內容要做強制了型別轉換才能使用。
(5)文件要求: 顯然介面方式相對比較清晰,訊息的話每個訊息都要詳細定義,並且嚴格按照該定義執行。
(6)可除錯性: 顯然介面方式要方便些,訊息很可能不小心就會引起混亂。
(7)監控過濾方便性:訊息方式走同一匯流排,可以很方便的增加過濾和監控功能, 介面方式則因為各個模組interface和Sink各不相同,增加這些功能沒那麼方便。
(8)跨執行緒或是跨程式,甚至跨機器呼叫:顯然介面方式基本做不到,訊息方式的話只要修改匯流排就可以做到。
(9)非同步支援: 訊息方式可以很方便的支援非同步,介面方式則做不到。

 

經過上面的比較, 我們可以得出一些結論:

訊息方式的強項是耦合性和擴充套件性,以及監控的方便性,個人感覺比較適合於Server端的大規模應用。
介面方式的強項是效能高效以及開發的方便性, 比較適用於同一程式內客戶端的小規模應用。

但是大部分時候, 對於架構師或是公司領導,他們會更關注可耦合性和可擴充套件性,所以他們會傾向於選擇訊息方式,儘管有時可能不是那麼適用。

另外,個人覺得編譯時靜態型別檢測是C++的優勢,能讓我們高效而可靠的開發程式,我們不應該放棄這些優勢而去把C++當作弱型別的動態語言來使用,我在 軟命令介面的適用場合 一文也有相關描述。

最後,對於該框架的設計,其實我個人傾向於上面2種方式的結合, 即各個模組的入介面(呼叫介面)走介面方式,而各模組內部觸發的事件走訊息匯流排的方式,雖然沒人採用這種方式,還是在這裡記錄一下。

一個多月了,訊息還是介面,領導們老大們關於走何種架構還在爭論中, 我等小兵就默默等待吧 ...

呵呵,不知道各位看客會作何選擇?

最近公司準備開發一個新產品,需要重新設計一套新的框架,但是就這框架中各模組的通訊方式,大家產生了爭論,主要集中在各模組的互動方式是訊息耦合還是介面耦合。

需求大概這樣,我們需要封裝一套客戶端SDK, 暴露一系列API給外部用,而這套SDK內部會有很多模組組成,這些模組之間相互會有互動。

第一種設計是基於介面耦合,框架如下:


這種介面方式的設計要點是:
a. 各模組以類似COM元件的方式封裝和暴露介面,也就是說模組會以介面的形式暴露介面,並且以Sink的方式通知外部事件。比如模組A的介面如下
class IA
{
public:
    virtual void fun1() = 0;
    virtual void fun2() = 0;
    .
    virtual void int AdviseSink(IASink* pSink) = 0;
    virtual void bool UnAdviseSink(int nCooki) = 0;
}

class IASink
{
public:
    virtual void Event1() = 0;
    virtual void Event2() = 0;
    .
}
b. 有一個統一的模組管理平臺來管理所有模組,可以通過該平臺查詢和載入需要的模組,然後後得到相應的介面指標進行操作。
c. 各模組間的互動全都通過直接呼叫其他模組的介面或是訂閱該模組的Sink來實現。

第二種設計是基於訊息耦合,框架如下:

這種訊息方式的設計要點是:
a. 各個模組只有一個訊息處理介面。 
    class IMessageHandler
    {
    public:
        virtual void ProcessMessage(Message& msg) = 0;
    };
b. 中間的訊息匯流排提供訊息的訂閱和分發,各個模組會向訊息匯流排註冊自己感興趣的訊息。
c. 需要呼叫某個模組介面 或是 觸發某個事件時,都是通過向訊息匯流排傳送訊息的方式, 然後由訂閱訊息的模組執行。

上面2種架構的設計方式各有優劣,下面我們來簡單比較一下:
(1)耦合性: 儘管基於介面的耦合已經降低了耦合性,但是相比訊息來說,顯然是訊息方式耦合性更弱。
(2)可擴充套件性: 某個模組新增加一個介面, 介面方式需要新加介面函式,而訊息方式只需要新加一個訊息型別。即使新增加一個模組,訊息方式只是新增加幾個訊息處理型別,非常方便。所以可擴充套件性來說,顯然也是訊息方式佔優。
(3)效能: 介面方式是直接呼叫,可是訊息方式需要經過訊息匯流排過濾分發, 顯然效能上介面方式更高。 
(4)編碼安全性,介面方式是強型別,介面一修改,編譯時就能很快發現問題;訊息方式卻是弱型別,訊息修改後,有可能要到執行時才能發現問題, 另外很多訊息內容要做強制了型別轉換才能使用。
(5)文件要求: 顯然介面方式相對比較清晰,訊息的話每個訊息都要詳細定義,並且嚴格按照該定義執行。
(6)可除錯性: 顯然介面方式要方便些,訊息很可能不小心就會引起混亂。
(7)監控過濾方便性:訊息方式走同一匯流排,可以很方便的增加過濾和監控功能, 介面方式則因為各個模組interface和Sink各不相同,增加這些功能沒那麼方便。
(8)跨執行緒或是跨程式,甚至跨機器呼叫:顯然介面方式基本做不到,訊息方式的話只要修改匯流排就可以做到。
(9)非同步支援: 訊息方式可以很方便的支援非同步,介面方式則做不到。

 

經過上面的比較, 我們可以得出一些結論:

訊息方式的強項是耦合性和擴充套件性,以及監控的方便性,個人感覺比較適合於Server端的大規模應用。
介面方式的強項是效能高效以及開發的方便性, 比較適用於同一程式內客戶端的小規模應用。

但是大部分時候, 對於架構師或是公司領導,他們會更關注可耦合性和可擴充套件性,所以他們會傾向於選擇訊息方式,儘管有時可能不是那麼適用。

另外,個人覺得編譯時靜態型別檢測是C++的優勢,能讓我們高效而可靠的開發程式,我們不應該放棄這些優勢而去把C++當作弱型別的動態語言來使用,我在 軟命令介面的適用場合 一文也有相關描述。

最後,對於該框架的設計,其實我個人傾向於上面2種方式的結合, 即各個模組的入介面(呼叫介面)走介面方式,而各模組內部觸發的事件走訊息匯流排的方式,雖然沒人採用這種方式,還是在這裡記錄一下。


相關文章