物件導向程式 低耦合、高內聚 – 耦合編 (Coupling)

1. 資料耦合 (Data Coupling)

模組間以簡單資料型別作為參數時,參數過多(3個)就會發生。
因為使用者必須傳遞所有參數到模組裡(即使是空資料也要傳null)。
可以拆分成多個函數,或是建立一個專門傳遞簡單資料型別的Class來儲存資料。


2. 結構耦合 (Stamp Coupling)

物件導向語言裡也稱物件耦合(Object Coupling),但有些書會單獨分一類出來。

模組間以資料結構/物件型別作為參數。
且對於資料結構/物件做的更改將影響到其他使用該資料結構/物件的模組
弱化方式:
 1. 使用interface作為參數類型。
 2. 只傳遞資料結構/物件內要用到的簡單資料作為參數。

// 壞例 :
public void sendEmail(Employee e, String text) {
		EmailUtil.send(e.getName(), e.getEmail(), text);
}
// 優解 :
public void sendEmail(String name, String email, String text) {
		EmailUtil.send(name, email, text);
}

3. 控制耦合 (Control Coupling)

依參數不同而使動作不同的相依關係,模組傳遞旗標去控制另一個模組內的內部邏輯。

// 壞例1 :
public void run(){
    takeAction(1);
}
public void takeAction(int key) {
		switch (key) {
        case 1:
            System.out.println("ONE RECEIVED");
            break;
        case 2:
            System.out.println("TWO RECEIVED");
            break;        
    }
}
// 優解1-1 :
public void run(){
    Printable printable = new PrinterOne();
    takeAction(printable);
}
public void takeAction(Printable printable){
    printable.print();
}
public interface Printable{
    void print();
}
public class PrinterOne implements Printable{
    @Override
    public void print() {
        System.out.println("ONE RECEIVED");
    }
}
public class PrinterTwo implements Printable{
    @Override
    public void print() {
        System.out.println("TWO RECEIVED");
    }
}
// 優解1-2 :
public static void main(String ... args){
    new Printer().takeAction(1);
}
@CommandsMapFactory
public class Printer {
    private final CommandsMap map = CommandsMap.of(this);
    public void takeAction(int key){
        map.execute(key);
    }
    @Command(1)
    void printOne() {
        System.out.println("ONE RECEIVED");
    }
    @Command(2)
    void printTwo() {
        System.out.println("TWO RECEIVED");
    }
}
//--------------------
// 壞例2 :
void updateView(boolean isError) {
    if (isError) {
        resultView.setVisible(true);
        errorView.setVisible(false);
        iconView.setImage(CROSS_MARK_IMAGE);
    } else {
        resultView.setVisible(false);
        errorView.setVisible(true);
        iconView.setImage(CHECk_MARK_IMAGE);
    }
}
// 優解2 :
void updateView(boolean isError) {
    resultView.setVisible(isError);
    errorView.setVisible(!isError);
    iconView.setImage(getIconImage(isError));
}
Image getIconImage(boolean isError):  return (!isError) ? CHECk_MARK_IMAGE : CROSS_MARK_IMAGE

4. 外部耦合 (External Coupling)

當對外部庫或系統有依賴關係時,就會發生這種耦合。
如:外部文件、裝置介面、通訊協定、資料格式


5. 共同耦合 (Common Coupling)

全域狀態的相依關係,如全域變數,兩模組使用相同的資料區且都可讀寫資料區內之資料。
可使用Singleton Pattern解決。
參考:https://skyyen999.gitbooks.io/-study-design-pattern-in-java/content/singleton.html
但Singleton也是共同耦合的一種,因為他也是全域可存取的。
參考:https://pttgame.com/gamedesign/M.1505060613.A.66F.html


6. 內容耦合 (Content Coupling)

一模組使用另一個模組內之部分程式碼或改變其他模組內的局部變數,極端例子為使用GoTo語法跳至特定Procedure。
即一個Class能直接修改另個Class的內部狀態,若不當修改狀態,則會另執行出錯或修改困難。

// 壞例1 :
public int sumValues(Calculator c){
    int result = c.getFirstNumber() + c.getSecondNumber();
    c.setResult(result);
    return c.getResult();
}
// 優解1 :
public int sumValues(Calculator c){
 //其他過程由Calculator內部執行
    c.sumAndUpdateResult();
    return c.getResult();
}
//--------------------
// 壞例2 :
public int callCalculator(Calculator c) {
		c.setParameter(42);
		c.prepare();
		c.calculate();
		c.tearDown();
		return c.getResult();
}
// 優解2 :
public int callCalculator(Calculator c) {
		//其他過程由Calculator內部執行
		c.setAndCalculateResult(42);
		return c.getResult();
}

參考文獻

  1. https://hackmd.io/@k139/r1y-9LmK4/%2Fs%2FH1x46U7KN?type=book#%E8%80%A6%E5%90%88%E5%8A%9B
  2. https://engineering.linecorp.com/zh-hant/blog/code-readability-vol4/
  3. https://www.slideshare.net/AdilAslam4/coupling-and-cohesion-in-software-engineering
  4. https://www.linkedin.com/pulse/types-coupling-ahmed-adel
  5. https://www.educative.io/edpresso/what-are-the-different-types-of-coupling
  6. http://tedlike.blogspot.com/2016/01/message-couplin.html