打造可維護軟體C# 讀書筆記
每個問題都是由一些小問題組成的。
-Martin Fowler
- 限制每個程式碼單元不超過4個邏輯分支點
- 將複雜的程式碼單元拆解成多個簡單的程式碼單元
- 邏輯分支點越少,程式碼單元越容易修改及測試
在C#中,下面陳述式與運算子都算分支點:
- if
- switch
- ? , ??
- && , ||
- while
- for , foreach
- try catch
最常見的分支點為 if 和 switch
書上使用取得國家國旗顏色來作為範例
static List<Color> getFlagColors(Nationality nationality){
switch(nationality){
case Nationality.DUTCH:
return new List<Color>{Color.Red,Color.White,Color.Blue};
case Nationality.GERMAN:
return new List<Color>{Color.Black,Color.Red,Color.Yellow};
case Nationality.BELGIAN:
return new List<Color>{Color.Black,Color.Yellow,Color.Red};
case Nationality.FRENCH:
return new List<Color>{Color.Blue,Color.White,Color.Red};
case Nationality.ITALIAN:
return new List<Color>{Color.Green,Color.White,Color.Red};
case Nationality.UNCLASSIFIED:
default:
return new List<Color>{Color.Gray};
}
}
switch語句必須依照國家回傳相對應的國旗顏色,現在只有五個國家加上一個未分類狀況,需要測試的獨立路徑數量為6。
就目前的數量看來,程式碼的可讀性沒有什麼問題,但是當國家數量一多,switch產生的壞味道(Switch Statements)就會出來了。
對此,書中的做法是將每一個國旗拆分到不同型別中:
首先定義一個通用的型別
public interface IFlag{
List<Color> Colors{get;}
}
接著針對不同國家定義不同國旗型別:
public class DutchFlag:IFlag{
public List<Color> Colors{
get{
return new List<Color>{Color.Red,Color.White,Color.Blue};
}
}
}
public class GermanFlag:IFlag{
public List<Color> Colors{
get{
return new List<Color>{Color.Black,Color.Red,Color.Yellow};
}
}
}
取得國旗的方法重構如下:
static Dictionary<Nationality,IFlag> iFlagColors = new Dictionary<Nationality,IFlag>()
{
{Nationality.DUTCH,new DutchFlag()},
{Nationality.GERMAN,new GermanFlag()},
};
static List<Color> getFlagColors(Nationality nationality){
IFlag flag;
if(iFlagColors.TryGetValue(nationality,out flag)){
return flag.Colors;
}
return new List<Color>{Color.Gray};
}
之後需要新增國家對應的國旗時,只需要實作新的國旗型別。不過,這種作法的缺點是:導致更多的程式碼散佈在更多類別中。
或是可以思考另一種重構方法
static Dictionary<Nationality,List<Color>> flagColors = new Dictionary<Nationality,List<Color>>()
{
{Nationality.DUTCH,new List<Color>{Color.Red,Color.White,Color.Blue}},
{Nationality.GERMAN,new List<Color>{Color.Black,Color.Red,Color.Yellow}},
{Nationality.BELGIAN,new List<Color>{Color.Black,Color.Yellow,Color.Red}},
{Nationality.FRENCH,new List<Color>{Color.Blue,Color.White,Color.Red}},
{Nationality.ITALIAN,new List<Color>{Color.Green,Color.White,Color.Red}},
{Nationality.UNCLASSIFIED,new List<Color>{Color.Gray}}
};
static List<Color> getFlagColorsByDictionary(Nationality nationality){
List<Color> result;
if(flagColors.TryGetValue(nationality,out result)){
return result;
}
return new List<Color>{Color.Gray};
}
直接將switch改成用Dictionary的方式實作。