什么是耦合和解耦


之前組內同學問我耦合的關系,我沒給對方講清楚,今天借這個機會來深入講講模塊之間的耦合關系這個事情。

本文將用圖文詳細講解七種耦合的不同之處。

高內聚與低耦合

高內聚與低耦合是每個軟件開發者追求的目標,那么內聚和耦合分別是什么意思呢?

內聚是從功能角度來度量模塊內的聯系,一個好的內聚模塊應當恰好做一件事。它描述的是模塊內的功能聯系。

耦合是軟件結構中各模塊之間相互連接的一種度量,耦合強弱取決於模塊間接口的復雜程度、進入或訪問一個模塊的點以及通過接口的數據。

耦合

不同模塊之間的關系就是耦合,根據耦合程度可以分為7種,耦合度依次變低。

  • 內容耦合
  • 公共耦合
  • 外部耦合
  • 控制耦合
  • 標記耦合
  • 數據耦合
  • 非直接耦合

下面我們來說說每種耦合是什么,開始之前先來說下要實現的功能。m1和m2是兩個獨立的模塊,其中m2種會顯示m1的輸入,m1會顯示m2的輸入。

很顯然,m1和m2兩個模塊之間會有一些聯系(耦合),你也可以想想如何實現這個功能,下面用7種不同的方式來實現這個功能。

注:項目的代碼我放到了github,項目的demo,可以在這里查看

內容耦合

如果發生下列情形,兩個模塊之間就發生了內容耦合。
一個模塊直接訪問另一個模塊的內部數據;
一個模塊不通過正常入口轉到另一模塊內部;
兩個模塊有一部分程序代碼重疊(只可能出現在匯編語言中);
一個模塊有多個入口。
在內容耦合的情形,所訪問模塊的任何變更,或者用不同的編譯器對它再編譯,
都會造成程序出錯。好在大多數高級程序設計語言已經設計成不允許出現內容
耦合。它一般出現在匯編語言程序中。這種耦合是模塊獨立性最弱的耦合。

內容耦合是最緊的耦合程度,一個模塊直接訪問另一模塊的內容,則稱這兩個模塊為內容耦合。

為了實現功能,我們將m1的輸入放到m2.m1input上,將m2的輸入放到m1.m2input上。

// m1.js
root.m2.m1input = this.value;
m2.update();

// m2.js
root.m1.m2input = this.value;
m1.update();

PS:不知道誰會這么寫代碼,除了我為了做演示之外。。。

查看完整代碼demo

公共耦合

若一組模塊都訪問同一個公共數據環境,則它們之間的耦合就稱為公共耦合。公共的數據環境可以是全局數據結構、共享的通信區、內存的公共覆蓋區等。 這種耦合會引起下列問題:
所有公共耦合模塊都與某一個公共數據環境內部各項的物理安排有關,若修改某個數據的大小,將會影響到所有的模塊。
無法控制各個模塊對公共數據的存取,嚴重影響軟件模塊的可靠性和適應性。
公共數據名的使用,明顯降低了程序的可讀性。
公共耦合的復雜程度隨耦合模塊的個數增加而顯著增加。若只是兩個模塊之間有公共數據環境,則公共耦合有兩種情況。
若一個模塊只是往公共數據環境里傳送數據,而另一個模塊只是從公共數據環境中取數據,則這種公共耦合叫做松散公共耦合。若兩個模塊都從公共數據環境中取數據,又都向公共數據環境里送數據,則這種公共耦合叫做緊密公共耦合。只有在模塊之間共享的數據很多,且通過參數表傳遞不方便時,才使用公共耦合。否則,還是使用模塊獨立性比較高的數據耦合好些。

一組模塊都訪問同一個全局數據結構,則稱之為公共耦合。

在這種case中,m1和m2將自己的輸入放到全局的data上。

// m1.js
root.data.m1input = this.value;
m2.update();

// m2.js
root.data.m2input = this.value;
m1.update();

查看完整代碼demo

外部耦合

一組模塊都訪問同一全局簡單變量而不是同一全局數據結構,而且不是通過參數表傳遞該全局變量的信息,則稱之為外部耦合。例如C語言程序中各個模塊都訪問被說明為extern類型的外部變量。外部耦合引起的問題類似於公共耦合,區別在於在外部耦合中不存在依賴於一個數據結構內部各項的物理安排。

一組模塊都訪問同一全局簡單變量,而且不通過參數表傳遞該全局變量的信息,則稱之為外部耦合。外部耦合和公共耦合很像,區別就是一個是簡單變量,一個是復雜數據結構。

在這種case中,m1和m2都將自己的輸入放到全局上。

// m1.js
root.m1input = this.value;
m2.update();

// m2.js
root.m2input = this.value;
m1.update();

查看完整代碼demo

控制耦合

如果一個模塊通過傳送開關、標志、名字等控制信息,明顯地控制選擇另一模塊的功能,就是控制耦合。這種耦合的實質是在單一接口上選擇多功能模塊中的某項功能。因此,對所控制模塊的任何修改,都會影響控制模塊。另外,控制耦合也意味着控制模塊必須知道所控制模塊內部的一些邏輯關系,這些都會降低模塊的獨立性。

模塊之間傳遞的不是數據信息,而是控制信息例如標志、開關量等,一個模塊控制了另一個模塊的功能。

從控制耦合開始,模塊的數據就放在自己內部了,不同模塊之間通過接口互相調用。

在這個case中,得增加一個需求,就是當m1的輸入為空時,隱藏m2的顯示信息。

// m1.js
root.m1input = this.value;
m2.update();

m2.toggle(!!this.value); // 傳遞flag

上面的代碼中m1直接控制了m2的顯示和隱藏。

查看完整代碼demo

標(印)記耦合

如果一組模塊通過參數表傳遞記錄信息,就是標記耦合。事實上,這組模塊共享了這個記錄,它是某一數據結構的子結構,而不是簡單變量。這要求這些模塊都必須清楚該記錄的結構,並按結構要求對此記錄進行操作。在設計中應盡量避免這種耦合,它使在數據結構上的操作復雜化了。如果采取“信息隱蔽”的方法,把在數據結構上的操作全部集中。

調用模塊和被調用模塊之間傳遞數據結構而不是簡單數據,同時也稱作特征耦合。

在這個case中,m1傳給m2的是一個對象。

// m1.js
me.m1input = this.value;
m2.update(me); // 傳遞引用

// m2.js
me.m2input = this.value;
m1.update(me);

查看完整代碼demo

數據耦合

如果一個模塊訪問另一個模塊時,彼此之間是通過數據參數(不是控制參數、公共數據結構或外部變量)來交換輸入、輸出信息的,則稱這種耦合為數據耦合。由於限制了只通過參數表傳遞數據,按數據耦合開發的程序界面簡單、安全可靠。因此,數據耦合是松散的耦合,模塊之間的獨立性比較強。在軟件程序結構中至少必須有這類耦合。

調用模塊和被調用模塊之間只傳遞簡單的數據項參數。相當於高級語言中的值傳遞。

在這個case中,m1傳給m2的是一個簡單數據結構。

// m1.js
me.m1input = this.value;
m2.update(me.m1input); // 傳遞值

// m2.js
me.m2input = this.value;
m1.update(me.m2input);

查看完整代碼demo

非直接耦合

兩個模塊之間沒有直接關系,它們之間的聯系完全是通過主模塊的控制和調用來實現的。耦合度最弱,模塊獨立性最強。

子模塊無需知道對方的存在,子模塊之間的聯系,全部變成子模塊和主模塊之間的聯系。

在這個case種,增加一個index.js作為主模塊。

// index.js
var m1 = root.m1;
var m2 = root.m2;

m1.init(function (str) {
    m2.update(str);
});

m2.init(function (str) {
    m1.update(str);
});

// m1.js
me.m1input = this.value;
inputcb(me.m1input); // inputcb是回調函數

// m2.js
me.m2input = this.value;
inputcb(me.m2input);

查看完整代碼demo

內聚

其實關於內聚也分為很多種,如下所示,如果你感興趣可以自己研究研究,我們下次再來分享內聚的問題。

  • 偶然內聚
  • 邏輯內聚
  • 時間內聚
  • 通信內聚
  • 順序內聚
  • 功能內聚

原文網址:http://yanhaijing.com/program/2016/09/01/about-coupling/

什么是解耦

解耦:假設生產者和消費者分別是兩個類。如果讓生產者直接調用消費者的某個方法,那么生產者對於消費者就會產生依賴(也就是耦合)。將來如果消費者的代碼發生變化,可能會影響到生產者。而如果兩者都依賴於某個緩沖區,兩者之間不直接依賴,耦合也就相應降低了。生產者直接調用消費者的某個方法,還有另一個弊端。由於函數調用是同步的(或者叫阻塞的),在消費者的方法沒有返回之前,生產者只好一直等在那邊。萬一消費者處理數據很慢,生產者就會白白糟蹋大好時光。緩沖區還有另一個好處。如果制造數據的速度時快時慢,緩沖區的好處就體現出來了。當數據制造快的時候,消費者來不及處理,未處理的數據可以暫時存在緩沖區中。等生產者的制造速度慢下來,消費者再慢慢處理掉。

因為太抽象,看過網上的說明之后,通過我的理解,我舉了個例子:吃包子。

假如你非常喜歡吃包子(吃起來根本停不下來),今天,你媽媽(生產者)在蒸包子,廚房有張桌子(緩沖區),你媽媽將蒸熟的包子盛在盤子(消息)里,然后放到桌子上,你正在看巴西奧運會,看到蒸熟的包子放在廚房桌子上的盤子里,你就把盤子取走,一邊吃包子一邊看奧運。在這個過程中,你和你媽媽使用同一個桌子放置盤子和取走盤子,這里桌子就是一個共享對象。生產者添加食物,消費者取走食物。桌子的好處是,你媽媽不用直接把盤子給你,只是負責把包子裝在盤子里放到桌子上,如果桌子滿了,就不再放了,等待。而且生產者還有其他事情要做,消費者吃包子比較慢,生產者不能一直等消費者吃完包子把盤子放回去再去生產,因為吃包子的人有很多,如果這期間你好朋友來了,和你一起吃包子,生產者不用關注是哪個消費者去桌子上拿盤子,而消費者只去關注桌子上有沒有放盤子,如果有,就端過來吃盤子中的包子,沒有的話就等待。

原文:https://www.jianshu.com/p/5543b2eee223


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM