你有一個函數,其中完全取決於參數值而採取不同行為。針對該參數的每個可能值,建立一個獨立的函數。 ...
本小節目錄
6Replace Parameter with Explicit Methods(以明確函數取代參數)
概要
你有一個函數,其中完全取決於參數值而採取不同行為。針對該參數的每個可能值,建立一個獨立的函數。
動機
如果某個參數有多種可能的值,而函數內又以表達式檢查這些參數值,並根據不同參數值做出不同的行為,就該使用本項重構;
可以獲得好處:“編譯期代碼檢查”,“介面更清楚”(如果用參數值決定函數行為,那麼函數用戶不但需要觀察該函數,而且還要判斷參數是否“合法化”。——而合法的參數,很少在文檔中提到,必須通過上下文,才能判斷);
考慮“編譯期檢驗”的好處,為了獲取一個清晰的介面,我們也值得這麼做。
範例
下列代碼根據不同的參數值,建立Employee之下不同的子類。
class EmployeeType { static Employee Create(int type) { switch (type) { case 0: return new Engineer(); case 1: return new Salesman(); case 2: return new Manager(); default: throw new ArgumentException("Incorrect type code value!"); } } } class Employee { } class Engineer:Employee { } class Salesman:Employee { } class Manager:Employee { }
由於這是一個工廠函數,不能實施Replace Conditional with Polymorphism,因為使用該函數時對象根本還沒有創建出來。由於可以預見到Employee不會有太多新的子類,所以可以放心地為每個子類建立一個工廠函數,而不必擔心工廠函數的數量會劇增。首先,根據參數值建立相應的新函數:
static Employee CreateEngineer() { return new Engineer(); } static Employee CreateSalesman() { return new Salesman(); } static Employee CreateManager() { return new Manager(); }
找到函數的調用端,把諸如下麵這樣的代碼:
Employee kent = EmployeeType.Create(0);
替換為:
Employee kent = EmployeeType.CreateEngineer();
替換完成之後,就可以可以刪掉Create()函數了。
小結
如果函數根據參數做出不同的響應,那麼就是需要重構的時候了。
7Preserve Whole Object(保持對象完整)
概要
你從某個對象中取出若幹值,將它們作為某一次函數調用中的參數,改為傳遞整個對象。
動機
我們常常會將來自同一對象的若幹數據項作為參數,傳遞給某個函數。這樣做的問題在於:萬一被調函數需要新的數據項,就必須查找並修改對此函數的所有調用。但是如果傳遞的是整個對象,則不會有此問題。
該項手法的好處:(1)使參數列更加穩固;(2)提高代碼的可讀性。
範例
class Room { public bool WithinPlan(HeatingPlan plan) { int low = DaysTempRange().GetLow(); int high = DaysTempRange().GetHigh(); return plan.WithinRange(low, high); } public TempRange DaysTempRange() { return new TempRange(); } } class HeatingPlan { private TempRange _range; public bool WithinRange(int low, int high) { return low >= _range.GetLow() && high <= _range.GetHigh(); } } class TempRange { public int GetLow() { return 6; } public int GetHigh() { return 28; } }
這裡其實不必將TempRange對象的信息拆開來單獨傳遞,只需將整個對象傳遞給WithinPlan()函數即可:
class Room { public bool WithinPlan(HeatingPlan plan) { return plan.WithinRange(DaysTempRange()); } public TempRange DaysTempRange() { return new TempRange(); } } class HeatingPlan { private TempRange _range; public bool WithinRange(TempRange roomRange) { return roomRange.GetLow() >= _range.GetLow() && roomRange.GetHigh() <= _range.GetHigh(); } } class TempRange { public int GetLow() { return 6; } public int GetHigh() { return 28; } }
小結
傳遞整個對象雖然好,但事情總是有兩面性。如果傳的是數值,被調用函數就只依賴於這些數值,而不依賴它們所屬的對象。但是如果傳遞的是整個對象,被調用函數所在的對象就需要依賴參數對象。如果這會使依賴結構惡化,就不該使用本項重構。
To Be Continued……