概述
一、Extract Method(提炼函数)
如果你有一段代码可以被组织在一起并独立出来,将这段代码放进一个独立函数中,并让函数名称解释该函数的用途。
做法:
- 创造一个新函数,根据这个函数的意图来给它命名(以它「做什么」来命名, 而不是以它「怎样做」命名)。
- 将提炼出的代码从源函数(source)拷贝到新建的目标函数(target)中。
- 仔细检查提炼出的代码,看看其中是否引用了「作用域(scope)限于源函数」的变量(包括局部变量和源函数参数)。
- 检查是否有「仅用于被提炼码」的临时变量(temporary variables )。如果有,在目标函数中将它们声明为临时变量。
- 检查被提炼码,看看是否有任何局部变量(local-scope variables )的值被它改变。如果一个临时变量值被修改了,看看是否可以将被提炼码处理为一个查询(query),并将结果赋值给相关变量。
- 将被提炼码中需要读取的局部变量,当作参数传给目标函数。
- 处理完所有局部变量之后,进行编译。
- 在源函数中,将被提炼码替换为「对目标函数的调用」。
- 如果你将任何临时变量移到目标函数中,请检查它们原本的声明式是否在被提炼码的外围。如果是,现在你可以删除这些声明式了。
- 编译,测试。
样例:
void printOwing(double previousAmount) {
Enumeration e = _orders.elements();
double outstanding = previousAmount * 1.2;
// 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);
}
Extract Method====》
void printOwing(double previousAmount) {
printBanner();
double outstanding = getOutstanding(previousAmount * 1.2);
printDetails(outstanding);
}
//对局部变量再赋值:
//如果这个变量在被提炼码之后未再被使用,只需直接在目标函数中修改它就可以了;
//如果被提炼码之后的代码还使用了这个变量,就需要让目标函数返回该变量改变后的值
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);
}
//无局部变量提炼
void printBanner() {
// print banner
System.out.println ("**************************");
System.out.println ("***** Customer Owes ******");
System.out.println ("**************************");
}
二、Inline Method(将函数内联化)
样例:
int getRating() {
return (moreThanFiveLateDeliveries()) ? 2 : 1;
}
boolean moreThanFiveLateDeliveries() {
return _numberOfLateDeliveries > 5;
}
Inline Method==》
int getRating() {
return (_numberOfLateDeliveries > 5) ? 2 : 1;
}
三、Inline Temp(将临时变量内联化)
场景:一个临时变量,只被一个简单表达式赋值一次,而它妨碍了其他重构手法。
做法:将所有对该变量的引用动作,替换为对它赋值的那个表达式本身。
样例:
double basePrice = anOrder.basePrice();
return (basePrice > 1000)
Inline Temp==》
return (anOrder.basePrice() > 1000)
四、Replace Temp with Query(以查询取代临时变量)
场景:程序以一个临时变量(temp)保存某一表达式的运算结果。
做法:将这个表达式提炼到一个独立函数中。将这个临时变量的所有「被引用点」替换为「对新函数的调用」。新函数可被其他函数使用。
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000)
return basePrice * 0.95;
else
return basePrice * 0.98;
Replace Temp with Query==》
if (basePrice() > 1000)
return basePrice() * 0.95;
else
return basePrice() * 0.98;
...
double basePrice() {
return _quantity * _itemPrice;
}
五、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);
}
Introduce Explaining Variable=>
double price() {
final double basePrice = _quantity * _itemPrice;
final double quantityDiscount = Math.max(0, _quantity - 500) * _itemPrice * 0.05;
final double shipping = Math.min(basePrice * 0.1, 100.0);
return basePrice - quantityDiscount + shipping;
}
Extract Method=>
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;
}
什么情况下使用Introduce Explaining Variable ?
答案:在需要花费更大工作量去 Extract Method 时。如果要处理的是一个拥有大量局部变量的算法,那么使用 Extract Method 绝非易事。在这种情况下,先使用Introduce Explaining Variable 理清代码,搞清楚代码逻辑之后,再运用 Replace Temp with Query 把被引入的那些解释性临时变量去掉。
六、Split Temporary Variable(剖解临时变量)
场景:有某个临时变量被赋值超过一次,但它既不是循环变量,也不是一个集用临时变量(collecting temporary variable)。
做法:针对每次赋值,创造一个独立的、对应的临时变量。
动机(Motivation)
很多临时变量用于保存一段冗长代码的运算结果,以便稍后使用。这种临时变量应该只被赋值一次。如果它们被赋值超过一次,就意味它们在函数中承担了一个以上的责任。如果临时变量承担多个责任,它就应该被替换为多个临时变量,每个变量只承担一个责任。同一个临时变量承担两件不同的 事情,会令代码阅读者糊涂。
样例:
在这个例子中,acc变量有两个责任:第一是保存第一个力造成的初始加速度;第二是保存两个力共同造成的加速度。
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;
}
Split Temporary Variable=>
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;
double acc = (_primaryForce + _secondaryForce) / _mass;
result += primaryVel * secondaryTime + 0.5 * acc * secondaryTime * secondaryTime;
}
return result;
}
七、Substitute Algorithm(替换你的算法)
场景:把某个算法替换为另一个更清晰的算法。
做法:将函数本体(method body)替换为另一个算法。
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";
}
if (people[i].equals ("Kent")){
return "Kent";
}
}
return "";
}
Substitute Algorithm=>
String foundPerson(String[] people){
List candidates = Arrays.asList(new String[] {"Don", "John", "Kent"});
for (int i=0; i<people.length; i++)
if (candidates.contains(people[i]))
return people[i];
return "";
}
最后
以上就是喜悦奇迹为你收集整理的重构:重新组织函数(《重构:改善既有代码的设计》第六章笔记)的全部内容,希望文章能够帮你解决重构:重新组织函数(《重构:改善既有代码的设计》第六章笔记)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复