返回總目錄 本小節目錄 Separate Query from Modifier(將查詢函數和修改函數分離) Parameterize Method(令函數攜帶參數) 4Separate Query from Modifier(將查詢函數和修改函數分離) 概要 某個函數既返回對象狀態值,又修改對象狀 ...
本小節目錄
4Separate Query from Modifier(將查詢函數和修改函數分離)
概要
某個函數既返回對象狀態值,又修改對象狀態(副作用)。
建立兩個不同的函數,其中一個負責查詢,另一個負責修改。
動機
任何有返回值的函數,都不應該有看得到的副作用。如果有一個函數“既有返回值又有副作用”,那麼就應該嘗試著將查詢動作從修改動作中分割出來。
範例
有這樣一個函數:一旦有人入侵安全系統,它會返回入侵者的姓名,併發送一個警報:
class Security { void CheckSecurity(string[] people) { string found = FoundMiscrent(people); } string FoundMiscrent(string[] people) { foreach (var person in people) { if (person == "Don") { SendAlert(); return "Don"; } if (person == "John") { SendAlert(); return "John"; } } return string.Empty; } void SendAlert() { } }
為了將查詢動作和修改動作分開,首先建立一個適當的查詢函數,使其與修改函數返回相同的值,但不造成任何副作用。
string FoundPerson(string[] people) { foreach (var person in people) { if (person == "Don") { return "Don"; } if (person == "John") { return "John"; } } return string.Empty; }
然後,逐一替換原函數內所有的return語句,該調用新建的查詢函數:
string FoundMiscrent(string[] people) { foreach (var person in people) { if (person == "Don") { SendAlert(); return FoundPerson(people); } if (person == "John") { SendAlert(); return FoundPerson(people); } } return FoundPerson(people); }
現在修改調用者,將原本的單一調用者替換為兩個調用:先調用修改函數,然後調用查詢函數:
void CheckSecurity(string[] people) { FoundMiscrent(people); string found = FoundPerson(people); }
替換完畢後,將修改函數的返回值改為void,並修改其函數名稱:
void SendAlert(string[] people) { foreach (var person in people { if (person == "Don") { SendAlert(); return; } if (person == "John") { SendAlert(); return; } } }
當然,這種情況下,得到了大量重覆代碼,因為修改函數之中使用了與查詢函數相同的代碼。現在對修改函數實施Substitute Algorithm,設法讓它再簡潔一些:
void SendAlert(string[] people) { if (FoundPerson(people) != "") { SendAlert(); } }
小結
如果某個函數只是向你提供一個值,沒有任何看得到的副作用,那麼你可以任意調用這個函數,也可以把調用動作搬移到函數的其他地方。簡而言之,需要操心的事情少多了。
5Parameterize Method(令函數攜帶參數)
概要
若幹函數做了類似的工作,但在函數本體中包含了不同的值。
建立單一函數,以參數表達那些不同的值。
動機
有這樣兩個函數:它們做著類似的工作,但因少數幾個值致使行為略有不同。這時,可以將這些各自分離的函數統一起來,並通過參數來處理那些變化的情況,用以簡化問題。
範例
先看一個簡單的例子:
class Employee { public double Salary { get; set; } void TenPercentRaise() { Salary *= 1.1; } void FivePercentRaise() { Salary *= 1.05; } }
這段代碼可以替換如下:
class Employee { public double Salary { get; set; }
void Raise(double factor) { Salary *= 1 + factor; } }
再來看一個稍微複雜的例子:
class Dollars { public Dollars(double result) { } } class Charge { private Dollars BaseCharge() { double result = Math.Min(LastUsage(), 100) * 0.03; if (LastUsage() > 100) { result += (Math.Min(LastUsage(), 200) - 100) * 0.05; } if (LastUsage() > 200) { result += (LastUsage() - 200) * 0.07; } return new Dollars(result); } private int LastUsage() { return 201; } }
上述代碼可以替換如下:
class Charge { private Dollars BaseCharge() { double result = UsageInRange(0, 100) * 0.03; result += UsageInRange(100, 200) * 0.05; result += UsageInRange(200, Int32.MaxValue) * 0.07; return new Dollars(result); } private int UsageInRange(int start, int end) { if (LastUsage() > start) { return Math.Min(LastUsage(), end) - start; } return 0; } private int LastUsage() { return 201; } }
本項重構的要點在於:以“可將少量數值視為參數”為依據,找出帶有重覆性的代碼。
小結
本項重構可以去除重覆代碼,並提高靈活性,因為可以用這個參數處理更多的變化情況。
To Be Continued……