打造可維護軟體C#-Ch03

打造可維護軟體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的方式實作。