重构手法之简化函数调用【3】
本小节目录
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……