今天接著介紹第二個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()
內選擇付款方式從兩種變四種。
現在我們知道了不能在既有
的程式碼做修改,也知道了業務邏輯與附加邏輯間的差異,那我們就可以開始對剛剛的付款範例進行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();
}
}
現在我們將原本的Paymant業務邏輯獨立出來一個abstract class,並把setPaymentType定義為抽象方法讓繼承的類別去實作他。接著把每個付款項目各自建立一個class繼承Payment,並且Override setPaymentType()。這樣若未來銀行想要增加虛擬貨幣付款,就不需要更動Payment的內容(符合修改封閉),直接建立一個新的class並繼承Payment即可(符合擴充開放)。
1. 降低耦合
2. 增加擴展性
3. 易於維護
擴充新功能應藉由新增新的程式碼,而非修改原本的程式碼
物件導向設計原則:開放封閉原則,定義、解析與實踐
[物件導向]開放封閉原則(Open/closed principle)
[ 設計原則 ] - 開放封閉原則(OCP,Open-Closed Principle)