重构-改善既有代码的设计完整笔记系列之6、7 - 重新组织函数、对象间搬迁特性
目录
- 6.1 Extract Method(提炼函数)
- 6.2 Inline Method(内联函数)
- 6.3 Inline Temp(内联临时变量)
- 6.4 Replace Temp with Query(以查询取代临时变量)
- 6.5 Introduce Explaining Variable(引入解释性变量)
- 6.6 Split Temporary Variable(分解临时变量)
- 6.7 Remove Assignments to Parameters(移除对参数的赋值)
- 6.8 Replace Method with Method Object(以函数对象取代函数)
- 6.9 Substitute Algorithm(替换算法)
- 7.1 Move Method(搬移函数)
- 7.2 Move Field(搬移字段)
- 7.3 Extract Class(提炼类)
- 7.4 Inline Class(将类内联化)
- 7.5 Hide Delegate(隐藏“委托关系”)
- 7.6 Remove Middle Man(移除中间人)
- 7.7 Introduce Foreign Method(引入外加函数)
- 7.8 Introduce Local Extension(引入本地扩展)
6.1 ExtractMethod(提炼函数)
将一段代码放进一个独立函数中,并让函数名称解释该函数的用途。
增加可读性,函数粒度小更容易被复用和覆写。
例
void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; // print banner System.out.println("**************************"); System.out.println("***** Customer Owes ******"); System.out.println("**************************"); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } // print details System.out.println("name:" + _name); System.out.println("amount" + outstanding); } 改 void printOwing(double previousAmount) { printBanner(); double outstanding = getOutstanding(previousAmount * 1.2); printDetails(outstanding); } void printBanner() { // print banner System.out.println("**************************"); System.out.println("***** Customer Owes ******"); System.out.println("**************************"); } double getOutstanding(double initialValue) { double result = initialValue; Enumeration e = _orders.elements(); while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); result += each.getAmount(); } return result; } void printDetails(double outstanding) { System.out.println("name:" + _name); System.out.println("amount" + outstanding); }
6.2 Inline Method(内联函数)
在函数调用点插入函数本体,然后移除该函数。
函数的本体与名称同样清楚易懂,间接层太多反而不易理解。
例
int getRating() { return (moreThanFiveLateDeliveries()) ? 2 : 1; } boolean moreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; } 改 int getRating() { return (_numberOfLateDeliveries > 5) ? 2 : 1; }
6.3 Inline Temp(内联临时变量)
将所有对该变量的引用动作,替换为对它赋值的那个表达式自身。
例
double basePrice = anOrder.basePrice(); return (basePrice > 1000) 改 return (anOrder.basePrice() > 1000)
6.4 Replace Temp with Query(以查询取代临时变量)
将一个表达式提炼到一个独立函数中,并将临时变量的引用点替换为对函数的调用。
临时变量扩展为查询函数,就可以将使用范围扩展到整个类。
减少临时变量,使函数更短更易维护。让原来的不容易理解的长函数分段化。
例
double getPrice() { int basePrice = _quantity * _itemPrice; double discountFactor; if (basePrice > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice * discountFactor; } 改 double getPrice() { return basePrice() * discountFactor(); } private double discountFactor() { if (basePrice() > 1000) return 0.95; else return 0.98; } private int basePrice() { return _quantity * _itemPrice; }
6.5 Introduce Explaining Variable(引入解释性变量)
将该复杂表达式的结果放进一个临时变量,以变量名来解释其用途。让冗长的表达式更加可读。
例
double price() { // price is base price - quantity discount + shipping return _quantity * _itemPrice - Math.max(0, _quantity - 500) * _itemPrice * 0.05 + Math.min(_quantity * _itemPrice * 0.1, 100.0); } 改 double price() { return basePrice() - quantityDiscount() + shipping(); } private double quantityDiscount() { return Math.max(0, _quantity - 500) * _itemPrice * 0.05; } private double shipping() { return Math.min(basePrice() * 0.1, 100.0); } private double basePrice() { return _quantity * _itemPrice; }
6.6 Split Temporary Variable(分解临时变量)
针对每次赋值,创造一个独立、对应的临时变量。
临时变量会被多次赋值,容易产生理解歧义。
如果变量被多次赋值(除了“循环变量”和“结果收集变量”),说明承担了多个职责,应该分解。
例
double getDistanceTravelled (int time) { double result; double acc = _primaryForce / _mass; int primaryTime = Math.min(time, _delay); result = 0.5 * acc * primaryTime * primaryTime; int secondaryTime = time - _delay; if (secondaryTime > 0) { double primaryVel = acc * _delay; acc = (_primaryForce + _secondaryForce) / _mass; result += primaryVel * secondaryTime + 0.5 * acc * secondaryTime * secondaryTime; } return result; } //acc被赋值了2次,代码比较乱 改 double getDistanceTravelled(int time) { double result; final double primaryAcc = _primaryForce / _mass; int primaryTime = Math.min(time, _delay); result = 0.5 * primaryAcc * primaryTime * primaryTime; int secondaryTime = time - _delay; if (secondaryTime > 0) { double primaryVel = primaryAcc * _delay; final double secondaryAcc = (_primaryForce + _secondaryForce) / _mass; result += primaryVel * secondaryTime + 0.5 * secondaryAcc * secondaryTime * secondaryTime; } return result; }
使用secondaryAcc变量来区分
6.7 Remove Assignments to Parameters(移除对参数的赋值)
以一个临时变量取代该参数的位置。
对参数赋值容易降低代码的清晰度;
容易混淆按值传递和按引用传递的方式。
int discount(int inputVal, int quantity, int yearToDate) { if (inputVal > 50) inputVal -= 2; if (quantity > 100) inputVal -= 1; if (yearToDate > 10000) inputVal -= 4; return inputVal; } 改 int discount(final int inputVal, final int quantity, final int yearToDate) { int result = inputVal; if (inputVal > 50) result -= 2; if (quantity > 100) result -= 1; if (yearToDate > 10000) result -= 4; return result; }
加上final,让它遵循不对参数赋值。
6.8 Replace Method with Method Object(以函数对象取代函数)
一个大型函数如果包含了很多临时变量,用Extract Method很难拆解,
可以把函数放到一个新创建的类中,把临时变量变成类的实体变量,再用Extract Method拆解。
感觉原书的例子不大贴切,略过。
6.9 Substitute Algorithm(替换算法)
复杂的算法会增加维护的成本,替换成较简单的算法实现,往往能明显提高代码的可读性和可维护性。
string foundPerson(string[] people){ for (int i = 0; i < people.length; i++){ if (people[i].equals("Don")){ return "Don"; } if (people[i].equals("John")){ return "John"; } } return ""; } 重构后 string foundPerson(string[] people){ list oList = Arrays.asList(new String[]{"Don", "John"}); for (int i = 0; i < people.length; i++){ if (oList.contains(people[i])) return people[i]; } return ""; }
感觉也有点不大贴切。
7.1 Move Method(搬移函数)
如果一个函数在另一个类中用的更多。在另一个类中添加一个有着类似行为的新函数,将旧函数变成一个单纯的委托函数或者将旧函数删除。
类的行为做到单一职责,不要越俎代庖。
如果一个类有太多行为,或一个类与另一个类有太多合作而形成高度耦合,就需要搬移函数。
观察调用它的那一端、它调用的那一端,已经继承体系中它的任何一个重定义函数。
根据“这个函数不哪个对象的交流比较多”,决定其移动路径。
我不大同意作者将透支函数搬到账户类型类,代码略。
7.2 Move Field(搬移字段)
某个字段被别一个类更多的用到,把这个类移到另一个类中,修改源字段的所有用户,令它们引用新的字段。
class A{ public: string m_field; } class B{ } 重构后 class A{ } class B{ publuc: string m_field; }
7.3 Extract Class(提炼类)
某个类做了应该由两个类做的事,建立一个新类,将相关字段和函数从旧类搬移到新类中。
职责单一,松耦合原则。
class Person { public String getName() { return _name; } public String getTelephoneNumber() { return ("(" + _officeAreaCode + ") " + _officeNumber); } String getOfficeAreaCode() { return _officeAreaCode; } void setOfficeAreaCode(String arg) { _officeAreaCode = arg; } String getOfficeNumber() { return _officeNumber; } void setOfficeNumber(String arg) { _officeNumber = arg; } private String _name; private String _officeAreaCode; private String _officeNumber; } 改 class Person { public String getName() { return _name; } public String getTelephoneNumber() { return _officeTelephone.getTelephoneNumber(); } TelephoneNumber getOfficeTelephone() { return _officeTelephone; } private String _name; private TelephoneNumber _officeTelephone = new TelephoneNumber(); } class TelephoneNumber { public String getTelephoneNumber() { return ("(" + _areaCode + ") " + _number); } String getAreaCode() { return _areaCode; } void setAreaCode(String arg) { _areaCode = arg; } String getNumber() { return _number; } void setNumber(String arg) { _number = arg; } private String _areaCode; private String _number; }
7.4 Inline Class(将类内联化)
正好于Extract Class (提炼类)相反。如果一个类不再承担足够责任、不再有单独存在的理由。将这个类的所有特性搬移到另一个类中,然后移除原类。
class Person { Department _department; public Department getDepartment() { return _department; } public void setDepartment(Department arg) { _department = arg; } } class Department { private String _chargeCode; private Person _manager; public Department(Person manager) { _manager = manager; } public Person getManager() { return _manager; } }
7.5 Hide Delegate(隐藏“委托关系”)
class Person { Department _department; public Department getDepartment() { return _department; } public void setDepartment(Department arg) { _department = arg; } } class Department { private String _chargeCode; private Person _manager; public Department(Person manager) { _manager = manager; } public Person getManager() { return _manager; } } //如果要获取某个员工的经理: manager = john.getDepartment().getManager(); //这就暴露了Department的原理,可以在Person类增加方法来减少耦合 public Person getManager() { return _department.getManager(); } //这样就可以直接调用了 manager = john.getManager();
7.6 Remove Middle Man(移除中间人)
某个类做了过多的简单委托动作,让客户直接调用受托类。
A->B->C 重构后 A->B A->C
7.7 Introduce Foreign Method(引入外加函数)
你需要为提供服务的类增加一个函数,但你无法修改这个类。
在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。
客户类使用Date类的接口,但Date类没有提供nextDay()的接口,也不能改Date的源码:
Date newStart = new Date(pre.getYear(), pre.getMonth(), pre.getDate() + 1); //重构后 Date newStart = nextDay(pre); private static Date nextDay(Date arg){ return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1); }
7.8 Introduce Local Extension(引入本地扩展)
你需要为服务类提供一些额外函数,但你无法修改这个类。
建立一个新类,使它包含这些额外函数。让这个扩展品成为源类的子类戒包装类。
目前维护的开源产品:https://gitee.com/475660