建议149:使用表驱动法避免过长的if和switch分支
随着代码变得复杂,我们很容易被过长的if和switch分支困扰。
一个类枚举类型Week如下:
enum Week { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }
如果要把Week的元素值用中文输出,简单而丑陋的方法也许是封装一个GetChineseWeek方法:
static string GetChineseWeek(Week week) { switch (week) { case Week.Monday: return "星期一"; case Week.Tuesday: return "星期二"; case Week.Wednesday: return "星期三"; case Week.Thursday: return "星期四"; case Week.Friday: return "星期五"; case Week.Saturday: return "星期六"; case Week.Sunday: return "星期日"; default: throw new ArgumentOutOfRangeException("week","星期值超出范围"); } }
之所以说这种方法太丑陋,是因为:
1)分支太长了,而且出现了重复代码。
2)不利于扩展。如果出现星期八、星期九怎么办?当然,星期制已经是固定的了,应该不会出现扩展情况。但是,换种情景来考虑,假设我们正在渲染动画怎么办?谁知道下一秒美工会提交我多少个动画呢?
一种解决方案是使用多态,它很好的符合了“开闭”原则。如果增加条件分支,不必修改源代码,直接增加子类就可以了。利用多态避免分支,这里暂且不表,本建议要采用的是“表驱动法”。
可以把表驱动简单理解为查字典。代码如下表示:
static string GetChineseWeek(Week week) { string[] chineseWeek = { "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日" }; return chineseWeek[(int) week]; } static void Main(string[] args) { Console.WriteLine(GetChineseWeek(Week.Friday)); }
这是一种按照索引值驱动的表驱动法。枚举元素代表的整型值,很容易和字符串数组索引结合起来,用两行语句就解决了GetChineseWeek方法。但是,这种方法有局限性,如果需要换成:星期一Mike打扫卫生、星期二Rose清理衣柜、星期三Mike和Rose没事可以吵吵架、星期四Rose要去Shopping,也就是说需求由静态属性变成了动态行为,那么事情就变得复杂了。
遇上这种情况,我们可能会想到使用多态,在这里依然使用表驱动法加上一点反射来实现这类动态的行为,代码如下:
class Program { static string ActionInTable(Week week) { string[] methods = { "Cleaning", "CleanCloset", "Quarrel", "Shopping", "Temp", "Temp", "Temp" }; return methods[(int)week]; } static void Main(string[] args) { SampleClass sample=new SampleClass(); var addMethod = typeof (SampleClass).GetMethod(ActionInTable(Week.Monday)); addMethod.Invoke(sample, null); } } class SampleClass { public void Cleaning() { Console.WriteLine("打扫"); } public void CleanCloset() { Console.WriteLine("整理衣橱"); } public void Quarrel() { Console.WriteLine("吵架"); } public void Shopping() { Console.WriteLine("购物"); } public void Temp() { Console.WriteLine("临时安排"); } }
表驱动法是一种设计思路,也可以称为模式。在实际编码中,不应局限于用索引去驱动行为,而应当根据实际情况灵活运用。
转自:《编写高质量代码改善C#程序的157个建议》陆敏技