iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 4
0

本文同步於個人部落格

今天接著介紹第二個Design Principle。那就廢話不多說直接開始吧!!

  • 定義


Software entities like classes, modules and functions should be open for extension but closed for modifications.

翻譯年糕:軟體實體像是類別、模組和函式,應該只對延展開放,但對修改關閉。

簡單來說,就是當系統需要被擴充時,應該藉由新增新的程式碼來擴充新功能,而非修改原本的程式碼來擴充新功能。

先來看個例子,我們以前到百貨公司買東西,可以刷信用卡付錢,也可以刷金融卡付錢(兩者的差別在於金融卡帳戶有錢才能刷)。對於銀行而言他們會判斷你用的是哪種卡,而去做扣款的動作。

public class Payment {
    private String msg;
    public void pay(){
        System.out.println(msg);
    }
    public void setPaymentType(String type){
        if ("creditCard".equals(type)) {
            msg = "選擇信用卡付錢!!";
        } else if ("debitCard".equals(type)) {
            msg = "選擇金融卡付錢!!";
        }
    }
}

如上述程式,銀行判斷是刷哪種卡,在用該類的進行付款。但隨著科技越來越進步,現在不只這兩種付費方式。電子支付、悠遊卡...等等都是現在可以選擇的付費方式。如果銀行要跟著時代進步,勢必也要增加付款的功能才行。

public class Payment {
    private String msg;
    public void pay(){
        System.out.println(msg);
    }
    public void setPaymentType(String type){
        String msg;
        if ("creditCard".equals(type)) {
            msg = "選擇信用卡付錢!!";
        } else if ("debitCard".equals(type)) {
            msg = "選擇金融卡付錢!!";
        } else if ("easyCard".equals(type)) {
            msg = "選擇悠遊卡付錢!!";
        } else if ("electronicPayment".equals(type)) {
            msg = "選擇電子支付!!";
        } else {
            msg = ("不支援付款選項");
        }
        this.msg = msg;
    }
}

public class Bank {
    public static void main(String args[]) {
     Payment p = new Payment();
     p.setPaymentType("creditCard");
     p.pay();
    }
}

output

選擇信用卡付錢!!

程式經過修改後,增加了悠遊卡以及電子支付的方法進Payment內了!但這顯然不是一個好方法。如果再擴充的時候,把原本的方法改錯了,這樣會造成系統的問題。所以我們需要一個讓系統方便擴充,但同時又不能影響到原本的程式方法。

  • 業務邏輯?附加邏輯?


剛剛有提到,我們不能在既有的程式碼內做擴充,因為存在著風險。那我們要如何去擴充程式?首先我們要先分清楚業務邏輯以及附加邏輯。

業務邏輯為程式的核心邏輯,基本上系統可能有80%以上是圍繞著它,如同上述程式碼中的pay()。不管你選擇怎樣付款,會後都會進行pay()的動作進行付款。而剩下20%就是附加邏輯。附加邏輯會隨著新需求而增加,如同setPaymentType()內選擇付款方式從兩種變四種。

  • 應用 Open/Closed Principle


現在我們知道了不能在既有的程式碼做修改,也知道了業務邏輯與附加邏輯間的差異,那我們就可以開始對剛剛的付款範例進行OCP的修改!

abstract class Payment {
    private String msg;
    public void pay(){
        this.msg = setPaymentType();
        System.out.println(msg);
    }
    public abstract String setPaymentType();
}

class CreditCard extends Payment {
    @Override
    public String setPaymentType() {
        return "選擇信用卡付錢!!";
    };
}

// 其他三種略...

public class Bank {
    public static void main(String args[]) {
     CreditCard cc = new CreditCard();
     cc.pay();
    }
}

OCP

現在我們將原本的Paymant業務邏輯獨立出來一個abstract class,並把setPaymentType定義為抽象方法讓繼承的類別去實作他。接著把每個付款項目各自建立一個class繼承Payment,並且Override setPaymentType()。這樣若未來銀行想要增加虛擬貨幣付款,就不需要更動Payment的內容(符合修改封閉),直接建立一個新的class並繼承Payment即可(符合擴充開放)。

  • 小結


OCP的優點
1. 降低耦合
2. 增加擴展性
3. 易於維護
OCP目標

擴充新功能應藉由新增新的程式碼,而非修改原本的程式碼

  • 範例程式碼


範例1:未使用OCP
範例2:使用OCP

  • References


物件導向設計原則:開放封閉原則,定義、解析與實踐
[物件導向]開放封閉原則(Open/closed principle)
[ 設計原則 ] - 開放封閉原則(OCP,Open-Closed Principle)


上一篇
[Day03] 單一職責原則 | Single Responsibility Principle
下一篇
[Day05] 里氏替換原則 | Liskov Substitution Principle
系列文
從生活中認識Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言