濫用面向對象,代碼部分或徹底地違背了面向對象編程原則。java
代碼中有一個複雜的 switch
語句或 if
序列語句。git
面向對象程序的一個最明顯特徵就是:少用 switch
和 case
語句。從本質上說,switch
語句的問題在於重複(if
序列也一樣如此)。你常會發現 switch
語句散佈於不一樣地點。若是要爲它添加一個新的 case
子句,就必須找到全部 switch
語句並修改它們。面向對象中的多態概念可爲此帶來優雅的解決辦法。程序員
大多數時候,一看到 switch
語句,就應該考慮以多態來替換它。github
提煉函數(Extract Method)
將 switch
語句提煉到一個獨立函數中,再以 搬移函數(Move Method)
將它搬移到須要多態性的那個類裏。switch
是基於類型碼來識別分支,這時能夠運用 以子類取代類型碼(Replace Type Code with Subclass)
或 以狀態/策略模式取代類型碼(Replace Type Code with State/Strategy)
。以多態取代條件表達式(Replace Conditional with Polymorphism)
了。以明確函數取代參數(Replace Parameter with Explicit Methods)
。引入 Null 對象(Introduce Null Object)
。
switch
操做只是執行簡單的行爲,就沒有重構的必要了。switch
常被工廠設計模式族(工廠方法模式(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); }
問題this
你的程序中,有個函數與其所駐類以外的另外一個類進行更多交流:調用後者,或被後者調用。spa
解決
在該函數最常引用的類中創建一個有着相似行爲的新函數。將舊函數變成一個單純的委託函數,或是舊函數徹底移除。
問題
你有一個不可變的類型碼,它會影響類的行爲。
解決
以子類取代這個類型碼。
問題
你有一個類型碼,它會影響類的行爲,但你沒法經過繼承消除它。
解決
以狀態對象取代類型碼。
問題
你手上有個條件表達式,它根據對象類型的不一樣而選擇不一樣的行爲。
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 it's normal subclass. plan = customer.getPlan();
臨時字段(Temporary Field)的值只在特定環境下有意義,離開這個環境,它們就什麼也不是了。
有時你會看到這樣的對象:其內某個實例變量僅爲某種特定狀況而設。這樣的代碼讓人不易理解,由於你一般認爲對象在全部時候都須要它的全部變量。在變量未被使用的狀況下猜想當初設置目的,會讓你發瘋。
一般,臨時字段是在某一算法須要大量輸入時而建立。所以,爲了不函數有過多參數,程序員決定在類中建立這些數據的臨時字段。這些臨時字段僅僅在算法中使用,其餘時候卻毫無用處。
這種代碼很差理解。你指望查看對象字段的數據,可是出於某種緣由,它們老是爲空。
提煉類(Extract Class)
將臨時字段和操做它們的全部代碼提煉到一個單獨的類中。此外,你能夠運用 以函數對象取代函數(Replace Method with Method Object)
來實現一樣的目的。引入 Null 對象(Introduce Null Object)
在「變量不合法」的狀況下建立一個 null 對象,從而避免寫出條件表達式。
問題
某個類作了不止一件事。
解決
創建一個新類,將相關的字段和函數從舊類搬移到新類。
問題
你有一個過長函數,它的局部變量交織在一塊兒,以至於你沒法應用提煉函數(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. //... } }
問題
你須要再三檢查某對象是否爲 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 it's normal subclass. plan = customer.getPlan();
子類僅僅使用父類中的部分方法和屬性。其餘來自父類的饋贈成爲了累贅。
有些人僅僅是想重用超類中的部分代碼而建立了子類。但實際上超類和子類徹底不一樣。
以委託取代繼承(Replace Inheritance with Delegation)
消除繼承。提煉超類(Extract Superclass)
將全部超類中對於子類有用的字段和函數提取出來,置入一個新的超類中,而後讓兩個類都繼承自它。
問題
某個子類只使用超類接口中的一部分,或是根本不須要繼承而來的數據。
解決
問題
兩個類有類似特性。
解決
爲這兩個類創建一個超類,將相同特性移至超類。
兩個類中有着不一樣的函數,卻在作着同一件事。
這種狀況每每是由於:建立這個類的程序員並不知道已經有實現這個功能的類存在了。
函數更名(Rename Method)
根據它們的用途從新命名。搬移函數(Move 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); }
問題
若干函數作了相似的工做,但在函數本體中卻包含了不一樣的值。
解決
創建單一函數,以參數表達哪些不一樣的值。
問題
兩個類有類似特性。
解決
爲這兩個類創建一個超類,將相同特性移至超類。