重构-改善既有代码的设计完整笔记系列之10、11 - 简化函数、处理概括关系
10.1 Rename Method(函数改名)
- 10.2 Add Parameter(添加参数)
- 10.3 Remove Parameter(移除参数)
- 10.4 Separate Query from Modifier(将查询函数和修改函数分离)
- 10.5 Parameterize Method(令函数携带参数)
- 10.6 Replace Parameter with Explicit Methods(以明确函数取代参数)
- 10.7 Preserve Whole Object(保持对象完整)
- 10.8 Replace Parameter with Methods(以函数取代参数)
- 10.9 Introduce Parameter Object(引入参数对象)
- 10.10 Remove Setting Method(移除设值函数)
- 10.11 Hide Method(隐藏函数)
- 10.12 Replace Constructor with Factory Method(以工厂函数取代构造函数)
- 10.13 Encapsulate Downcast(封装向下转型)
- 10.14 Replace Error Code with Exception(以异常取代错误码)
- 10.15 Replace Exception with Test(以测试取代异常)
- 11.1 Pull Up Field(字段上移)
- 11.2 Pull Up Method(函数上移)
- 11.3 Pull Up Constructor Body(构造函数本体上移)
- 11.4 Push Down Method(函数下移)
- 11.5 Push Down Field(字段下移)
- 11.6 Extract Subclass(提炼子类)
- 11.7 Extract Superclass(提炼超类)
- 11.8 Extract Interface(提炼接口)
- 11.9 Collapse Hierarchy(折叠继承体系)
- 11.10 Form Tem Plate Method(塑造模板函数
- 11.11 Replace Inheritance with Delegation(以委托取代继承)
- 11.12 Replace Delegation with Inheritance(以继承取代委托)
10.1 Rename Method(函数改名)
10.2 Add Parameter(添加参数)
10.3 Remove Parameter(移除参数)
10.4 Separate Query from Modifier(将查询函数和修改函数分离)
略
10.5 Parameterize Method(令函数携带参数)
若干函数做了类似的工作,但在函数本体中却包含了不同的值。
建立一个单一函数,以参数表达那些不同的值。
类似函数提取
protected 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); } //替换为 protected Dollars baseCharge() { double result = usageInRange(0, 100) * 0.03; result += usageInRange(100, 200) * 0.05; result += usageInRange(200, Integer.MAX_VALUE) * 0.07; return new Dollars(result); } protected int usageInRange(int start, int end) { if (lastUsage() > start) return Math.min(lastUsage(), end) - start; else return 0; }
10.6 Replace Parameter with Explicit Methods(以明确函数取代参数)
某个函数完全取决于参数值而采取不同行为,为了获得一个清晰的接口,
针对该参数的每一个可能值,建立一个独立函数。
static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; static Employee create(int type) { switch (type) { case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager(); default: throw new IllegalArgumentException("Incorrect type code value"); } } //打散为独立函数: static Employee createEngineer() { return new Engineer(); } static Employee createSalesman() { return new Salesman(); } static Employee createManager() { return new Manager(); }
10.7 Preserve Whole Object(保持对象完整)
如果从对象中取出若干值,将它们作为某一次函数调用时的参数。改为传递整个对象。
除了可以使参数列更稳固外,还能简化参数列表,提高代码的可读性。
此外,使用完整对象,被调用函数可以利用完整对象中的函数来计算某些中间值。
int low = daysTempRange().getLow(); int high = daysTempRange().getHigh(); withinPlan = plan.withinRange(low, high); //替换: withinPlan = plan.withinRange(daysTempRange());
10.8 Replace Parameter with Methods(以函数取代参数)
对象调用某个函数,并将所得结果作为参数,传递给另一个函数。
而接受该参数的函数本身也能够调用前一个函数。
让参数接受者去除该项参数,并直接调用前一个函数。
public double getPrice() { int basePrice = _quantity * _itemPrice; int discountLevel; if (_quantity > 100) discountLevel = 2; else discountLevel = 1; double finalPrice = discountedPrice(basePrice, discountLevel); return finalPrice; } private double discountedPrice(int basePrice, int discountLevel) { if (discountLevel == 2) return basePrice * 0.1; else return basePrice * 0.05; } //看出2个方法里的2个重复的discountLevel判断了吗?我们可以减少一个discountLevel参数,增加一个函数,让条理更清晰。 private double getPrice() { if (getDiscountLevel() == 2) return getBasePrice() * 0.1; else return getBasePrice() * 0.05; } private int getDiscountLevel() { if (_quantity > 100) return 2; else return 1; } private double getBasePrice() { return _quantity * _itemPrice; } //以上是原书的例子,我认为0.1和0.05这种数值出现的有点别扭,其实还是可以这么改: private double getPrice() { double finalPrice = getDiscountPrice(getBasePrice()); return finalPrice; } private double getDiscountPrice(double basePrice) { if (_quantity > 100) return getBasePrice() * 0.1; else return getBasePrice() * 0.05; } private double getBasePrice() { return _quantity * _itemPrice; } //是不是逻辑更清楚了?
10.9 Introduce Parameter Object(引入参数对象)
如果一组参数总是一起被传递,以一个对象取代这些参数
10.10 Remove Setting Method(移除设值函数)
class Account { private String _id; Account(String id) { setId(id); } void setId(String arg) { _id = arg; } } //可以这么改,禁止设置ID class Account { private final String _id; Account(String id) { _id = id; } } //设置ID class Account { private String id; Account(String id) { setId(id); } private void setId(String arg) { id = "aaa" + arg; } }
10.11 Hide Method(隐藏函数)
10.12 Replace Constructor with Factory Method(以工厂函数取代构造函数)
这个和上一章的不变对象的例子相似。
class Person... static Person createMale(){ return new Male(); } static Person createFemale() { return new Female(); } Person kent = new Male(); //替换 Person kent = Person.createMale();
10.13 Encapsulate Downcast(封装向下转型)
10.14 Replace Error Code with Exception(以异常取代错误码)
10.15 Replace Exception with Test(以测试取代异常)
11.1 Pull Up Field(字段上移)
11.2 Pull Up Method(函数上移)
略
11.3 Pull Up Constructor Body(构造函数本体上移)
各个子类中拥有一些构造函数,它们的本体几乎完全一致。
在超类中新建一个构造函数,并在子类构造函数中调用它。
class Manager extends Employee... public Manager (String name, String id, int grade) { _name = name; _id = id; _grade = grade; } //替换为 public Manager (String name, String id, int grade) { super (name, id); _grade = grade; } //由Employee完成部分构造
11.4 Push Down Method(函数下移)
11.5 Push Down Field(字段下移)
略
11.6 Extract Subclass(提炼子类)
类中的某些特性只被某些实例用到。新建一个子类,将这部分特性移到子类中
class JobItem { private int _unitPrice; private int _quantity; private Employee _employee; private boolean _isLabor; public JobItem(int unitPrice, int quantity, boolean isLabor, Employee employee) { _unitPrice = unitPrice; _quantity = quantity; _isLabor = isLabor; _employee = employee; } public int getTotalPrice() { return getUnitPrice() * _quantity; } public int getUnitPrice() { return (_isLabor) ? _employee.getRate() : _unitPrice; } public int getQuantity() { return _quantity; } public Employee getEmployee() { return _employee; } } class Employee { public Employee(int rate) { _rate = rate; } public int getRate() { return _rate; } private int _rate; } //使用JobItem来对雇员结算报酬,如果是计时体力活(_isLabor),那么单价(getUnitPrice())就在Employee中设置价格,否则就用_unitPrice。 //其实以上代码比较短,看起来没问题 //目前是只有一个类型参数true: JobItem j1 = new JobItem (0, 5, true, kent); //如果条件逻辑再多七八个,那就容易乱了。 //我们增加JobItem一个构造函数 public JobItem (int unitPrice, int quantity) { this (unitPrice, quantity, false, null) } //这样就可以直接调用 JobItem j2 = new JobItem (10, 15); //注意,这个j2实例不是计时体力活 //增加一个子类 class LaborItem public LaborItem (int quantity, Employee employee) { super (0, quantity, true, employee); }
11.7 Extract Superclass(提炼超类)
两个类有相似特性。为这2个类建立一个超类,将相同特性移至超类。
避免重复代码。
11.8 Extract Interface(提炼接口)
面向接口编程,大部分的设计模式都需要它。
11.9 Collapse Hierarchy(折叠继承体系)
也即取消一些多余的子类
11.10 Form Tem Plate Method(塑造模板函数)
一些子类,其中相应的某些函数以相同的顺序执行类似的操作,但各个操作的细节不同。
将这些操作分别放迚独立的函数中,并保持它们都有相同的签名,于是原函数也就变得相同了,然后将原函数上移至超类。
11.11 Replace Inheritance with Delegation(以委托取代继承)
某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。
在子类中新建一个字段用以保存超类;调整子类函数,令它改而委托超类;然后去掉2者之间的继承关系。
11.12 Replace Delegation with Inheritance(以继承取代委托)
两个类之间使用委托关系,并经常为整个接口编写许多简单的委托函数。让委托类继承受托类。
目前维护的开源产品:https://gitee.com/475660