OCP原則是說,軟體實體應該開放擴充性,但要封閉修改需求。要達到這原則,便要善用物件導向的三大特性:封裝、多型和繼承,並配合介面的設計。
OCP是物件導向設計的主要精神,不過不可能所有的模組都滿足OCP原則,我們只能盡可能地降低未滿足OCP原則的模組數量,以達到最大程度的reusability和maintainability。
用一個範例程式來看OCP,考慮下列程式碼,這是一個購物車內計算總金額的method:
public class ShoppingCar {
public double totalPrice(Part[] parts) {
double total = 0.0;
for (int i = 0; i < parts.length; i++) {
System.out.println(parts[i].getPrice());
total += parts[i].getPrice();
}
return total;
}
}思考一下這method,參數是一個Part陣列,我們可以透過這個陣列取得每個Part的金額,未來要加入新的零件時,只要實作Part介面(或繼承Part)即可。到此為止符合了OCP原則,如果一切都這麼順利…
但是事情往往不是這樣,今天會計部門說,主機板漲了45%,記憶體漲了27%,我們可能會做這樣的修改
public double totalPrice(Part[] parts) {
double total = 0.0;
for (int i=0; i<parts.length; i++) {
if (parts[i] instanceof Motherboard)
total += (1.45 * parts[i].getPrice());
else if (parts[i] instanceof Memory)
total += (1.27 * parts[i].getPrice());
else
total += parts[i].getPrice();
}
return total;
}在巡訪的過程中去判斷Part物件,並判斷物件型態來實行不同的價格政策,不過,這樣的修改違反了OCP原則,因為我們修改了totalPrice的內容。此外,誰知道未來會計部門會不會提出其他需求?只要有新的需求,totalPrice()就要修改一次,這樣只會造成邏輯的混亂。(加入新的method,也不會是一個好的方法)
第一次Refactor
思考一下Part的部分,將Part抽象化,建立以下的程式碼
public class Part {
private double basePrice;
public void setPrice(double price) {
basePrice = price;
}
public double getPrice() {
return basePrice;
}
}接著,根據每個不同的零件來建立Part的子物件,並覆寫getPrice()
public class ConcretePart extends Part {
@Override
public double getPrice() {
return (1.45 * basePrice);
}
}這樣看起來可以解決totalPrice()的問題-不在totalPrice中判斷不同的零件。不過我們還是無法避免當某個零件價格改變時要去修改程式碼的問題,例如上述ConcretePart若是主機板,當主機板的價格不再調漲45%,那我們就要修改1.45,這樣也違反了OCP。
第二次Refactor
一個更好的方法是,在Part中加入PricePolicy物件
public class Part {
private PricePolicy pricePolicy;
public void setPricePolicy(PricePolicy policy) {
pricePolicy = policy;
}
public void setPrice(double price) {
pricePolicy.setPrice(price);
}
public double getPrice() {
return pricePolicy.getPrice();
}
}我們將Price屬性委託給PricePolicy
public class PricePolicy {
private double basePrice;
public void setPrice(double price) {
basePrice = price;
}
public double getPrice() {
return basePrice;
}
}接著,實作一個SalePrice繼承PricePolicy,SalePrice是一個特價的價格政策,
public class SalePrice extends PricePolicy {
private double discount;
public void setDiscount(double discount) {
this.discount = discount;
}
@Override
public double getPrice() {
double basePrice = super.getPrice();
return (basePrice * discount);
}
}現在,我們可以建立一些Part物件,並設定該物件的特殊定價
ShoppingCar car = new ShoppingCar();
Part motherboard = new Part();
SalePrice salePolicy = new SalePrice();
salePolicy.setDiscount(0.95);
motherboard.setPricePolicy(salePolicy);
motherboard.setPrice(1000);
Part memory = new Part();
PricePolicy normalPolicy = new PricePolicy();
memory.setPricePolicy(normalPolicy);
memory.setPrice(300);
Part[] parts = {motherboard, memory};
double totalPrice = car.totalPrice(parts);
UML的圖形如下






0 意見:
張貼意見