Switch 聲明(Switch Statements)你有一個複雜的 switch 語句或 if 序列語句。
Switch 聲明(Switch Statements)
switch
if
面向對象程序的一個最明顯特徵就是:少用 switch 和 case 語句。從本質上說,switch 語句的問題在於重複(if 序列也同樣如此)。你常會發現 switch 語句散佈於不同地點。如果要為它添加一個新的 case 子句,就必須找到所有 switch語句並修改它們。面向對象中的多態概念可為此帶來優雅的解決辦法。
case
大多數時候,一看到 switch 語句,就應該考慮以多態來替換它。
提煉函數(Extract Method)
搬移函數(Move Method)
以子類取代類型碼(Replace Type Code with Subclass)
以狀態/策略模式取代類型碼(Replace Type Code with State/Strategy)
以多態取代條件表達式(Replace Conditional with Polymorphism)
以明確函數取代參數(Replace Parameter with Explicit Methods)
引入 Null 對象(Introduce Null Object)
工廠方法模式(Factory Method)
抽象工廠模式(Abstract Factory)
問題
你有一段代碼可以組織在一起。
void printOwing() { printBanner();
//print details System.out.println("name: " + name); System.out.println("amount: " + getOutstanding()); }
解決
移動這段代碼到一個新的函數中,使用函數的調用來替代老代碼。
void printOwing() { printBanner(); printDetails(getOutstanding()); }
void printDetails(double outstanding) { System.out.println("name: " + name); System.out.println("amount: " + outstanding); }
你的程序中,有個函數與其所駐類之外的另一個類進行更多交流:調用後者,或被後者調用。
在該函數最常引用的類中建立一個有著類似行為的新函數。將舊函數變成一個單純的委託函數,或是舊函數完全移除。
你有一個不可變的類型碼,它會影響類的行為。
以子類取代這個類型碼。
你有一個類型碼,它會影響類的行為,但你無法通過繼承消除它。
以狀態對象取代類型碼。
你手上有個條件表達式,它根據對象類型的不同而選擇不同的行為。
class Bird { //... double getSpeed() { switch (type) { case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed() - getLoadFactor() * numberOfCoconuts; case NORWEGIAN_BLUE: return (isNailed) ? 0 : getBaseSpeed(voltage); } throw new RuntimeException("Should be unreachable"); } }
將這個條件表達式的每個分支放進一個子類內的覆寫函數中,然後將原始函數聲明為抽象函數。
abstract class Bird { //... abstract double getSpeed(); }
class European extends Bird { double getSpeed() { return getBaseSpeed(); } } class African extends Bird { double getSpeed() { return getBaseSpeed() - getLoadFactor() * numberOfCoconuts; } } class NorwegianBlue extends Bird { double getSpeed() { return (isNailed) ? 0 : getBaseSpeed(voltage); } }
// Somewhere in client code speed = bird.getSpeed();
你有一個函數,其中完全取決於參數值而採取不同的行為。
void setValue(String name, int value) { if (name.equals("height")) { height = value; return; } if (name.equals("width")) { width = value; return; } Assert.shouldNeverReachHere(); }
針對該參數的每一個可能值,建立一個獨立函數。
void setHeight(int arg) { height = arg; } void setWidth(int arg) { width = arg; }
你需要再三檢查某對象是否為 null。
if (customer == null) { plan = BillingPlan.basic(); } else { plan = customer.getPlan(); }
將 null 值替換為 null 對象。
class NullCustomer extends Customer { Plan getPlan() { return new NullPlan(); } // Some other NULL functionality. }
// Replace null values with Null-object. customer = (order.customer != null) ? order.customer : new NullCustomer();
// Use Null-object as if its normal subclass. plan = customer.getPlan();
臨時欄位(Temporary Field)的值只在特定環境下有意義,離開這個環境,它們就什麼也不是了。
有時你會看到這樣的對象:其內某個實例變數僅為某種特定情況而設。這樣的代碼讓人不易理解,因為你通常認為對象在所有時候都需要它的所有變數。在變數未被使用的情況下猜測當初設置目的,會讓你發瘋。 通常,臨時欄位是在某一演算法需要大量輸入時而創建。因此,為了避免函數有過多參數,程序員決定在類中創建這些數據的臨時欄位。這些臨時欄位僅僅在演算法中使用,其他時候卻毫無用處。 這種代碼不好理解。你期望查看對象欄位的數據,但是出於某種原因,它們總是為空。
提煉類(Extract Class)
以函數對象取代函數(Replace Method with Method Object)
某個類做了不止一件事。
建立一個新類,將相關的欄位和函數從舊類搬移到新類。
你有一個過長函數,它的局部變數交織在一起,以致於你無法應用提煉函數(Extract Method) 。
class Order { //... public double price() { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; // long computation. //... } }
將函數移到一個獨立的類中,使得局部變數成了這個類的欄位。然後,你可以將函數分割成這個類中的多個函數。
class Order { //... public double price() { return new PriceCalculator(this).compute(); } }
class PriceCalculator { private double primaryBasePrice; private double secondaryBasePrice; private double tertiaryBasePrice;
public PriceCalculator(Order order) { // copy relevant information from order object. //... }
public double compute() { // long computation. //... } }
異曲同工的類(Alternative Classes with Different Interfaces)兩個類中有著不同的函數,卻在做著同一件事。
異曲同工的類(Alternative Classes with Different Interfaces)
這種情況往往是因為:創建這個類的程序員並不知道已經有實現這個功能的類存在了。
函數改名(Rename Method)
添加參數(Add Parameter)
令函數攜帶參數(Parameterize Method)
提煉超類(Extract Superclass)
函數的名稱未能恰當的揭示函數的用途。
class Person { public String getsnm(); }
修改函數名。
class Person { public String getSecondName(); }
問題 某個函數需要從調用端得到更多信息。
class Customer { public Contact getContact(); }
解決 為此函數添加一個對象函數,讓改對象帶進函數所需信息。
class Customer { public Contact getContact(Date date); }
若干函數做了類似的工作,但在函數本體中卻包含了不同的值。
建立單一函數,以參數表達哪些不同的值。
兩個類有相似特性。
為這兩個類建立一個超類,將相同特性移至超類。
被拒絕的饋贈(Refused Bequest)子類僅僅使用父類中的部分方法和屬性。其他來自父類的饋贈成為了累贅。
被拒絕的饋贈(Refused Bequest)
有些人僅僅是想重用超類中的部分代碼而創建了子類。但實際上超類和子類完全不同。
以委託取代繼承(Replace Inheritance with Delegation)
某個子類只使用超類介面中的一部分,或是根本不需要繼承而來的數據。