《重构-改善既有代码的设计》阅读笔记
一直以来,总觉得自己只会纯粹地码代码,对代码没有自己的一种设计思想,久而久之,总觉得思维被固化。针对这种情况,给自己定了个目标,阅读两本关于代码设计书籍,第一本当然是设计模式相关,第二本就是本篇以之为主题将要陈述读后感的《重构-改善既有代码的设计》。
先来总体说下看完本书后对我个人的感触,主要有这两点:
- 发现自己其实已经具备了判断代码是否具有Bad Smell的能力;
- 重构不只是一次对项目的大型重构,也表现在对一段你看不惯的代码进行重新设计和组织;
- 重构随时可发生。
整本书是按照以下这张图进行组织,主要介绍了何为重构,为何重构,何时重构,如何重构。
提纲.png
书中介绍了很多重构手法,主要分为以下几大块:
重构方式.png
针对每大块,作者进行了细分,列举了很多重构手法说明、动机和目的,这本书可以当做一本字典,没事翻一翻。当真遇到某种某种重构场景,可再详细查阅重构步骤。这里只介绍几种我认为比较常见和有用的重构手法。在陈述重构手法前,先看下书中所陈述的,什么样的代码具有坏代码的味道。
坏代码的味道
坏代码味道.png总的来说,坏代码主要表现为以下几点:
- 函数过长;
- 类责任太多;
- 类之间的耦合度太高;
- switch / if-else 条件语句过长且逻辑重复。
重构函数
- 提炼函数 - Extract Method
场景:你有一段代码可以被组织在一起并独立起来。将这段代码放进一个独立函数中,并让函数名称解释该函数的用途。
重构前:
public void printInfo() {
printBaseInfo();
System.out.println("job = " + _job);
System.out.println("_salary = " + _salary);
}
private void printBaseInfo() {
System.out.println("name = " + _name);
System.out.println("age = " + _age);
}
重构后:
public void printInfo() {
printBaseInfo();
printExtraInfo();
}
private void printBaseInfo() {
System.out.println("name = " + _name);
System.out.println("age = " + _age);
}
private void printExtraInfo() {
System.out.println("job = " + _job);
System.out.println("_salary = " + _salary);
}
- 以查询取代临时变量 - ReplaceTemp with Query
场景:你的程序以一个临时变量保存一个表达式的结果。将这个表达式提炼到一个独立函数中。将这个临时变量的所有引用点替换为对新函数的调用。
重构前:
double basePrice = _price * _quantity;
if (basePrice > 1000) {
return basePrice * 0.7f;
} else {
return basePrice;
}
重构后:
if (basePrice() > 1000) {
return basePrice() * 0.7f;
} else {
return basePrice();
}
...
double basePrice() {
return _price * _quantity;
}
重构类
- 搬移函数/字段 - Move Method / Field
场景:你的程序中,某个函数/字段被其所驻类之外的另一个类更多地用到。在目标类新建一个函数/字段,修改源函数/字段的所有用户,令其使用新的函数/字段。
这里拿实际项目中我认为需要用到此类重构方式的例子来说明。根据返回的 status = (10, 20)订单被拒绝时展示拒绝按钮btnRejected,否则就隐藏。
重构前:
Activity 中调用:btnRejected.setVisibility(order.getStatus() == 10 || order.getStatus() == 20);
public class Order {
private int status;
public int getStatus() {
return status;
}
}
重构后:
Activity 中调用:btnRejected.setVisibility(order. isRejected());
public class Order {
private int status;
public int getStatus() {
return status;
}
public boolean isRejected() {
return status == 10 || status == 20;
}
}
-
隐藏委托关系 - Hide Delegate
image.png
场景:客户通过一个委托类来调用另一个对象。在服务类上建立客户所需的所有函数,用以隐藏委托关系。
上述图示直接陈述了委托关系,对客户隐藏委托关系,就不需要在服务器中公开被委托对象。这里可以将Client类对delegate类的引用 转为直接对 Server类的引用,然后由 Server 类委托 delegate 类的所有方法。
这里暂时不写案例。我在看这本书时,一直以为委托和组合是同一种。书中对委托提到的次数比较多。我自己看设计模式,其中有一条设计模式原则:多用组合少用继承。这个着实很令人疑惑,后来查阅,得知委托和组合其实并不是同一种。这里简单介绍下组合和委托,如以下两图所示。
组合类图.png
委托类图.png
对比可发现,委托相对于组合,是在受托方法中加入了委托对象,最后实际调用的还是受托者的方法。
简化条件表达式
- 以多态取代条件表达式 - Replace Conditional with Polymorphism
场景:项目中条件表达式,根据对象类型的不通而选择不同的行为。将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。
重构前:
public class Employee {
public int payAmount(int type) {
switch (type) {
case ENGINEER:
return salary + comission;
case SALESMAN:
return salary + bonus;
}
return 0;
}
重构后,以一张类图展示多态替换条件表达式的好处:
多态替换表达式后的类图.png
简化函数调用
-
分离查询函数和修改函数 - Separate Query from Modifier
场景:某个函数既返回对象状态值,又修改对象状态。建立两个不同的函数,其中一个负责查询,另一个负责修改。
此重构方式,遵循一条规则:任何有返回值的函数,都不应该有看得到的副作用。可以把这种方式看做把 get方法 和 set方法分开。 -
引入参数对象 - Introduce Parameter Object
场景:某些参数总是很自然地同时出现。以一个对象取代这些参数。
本项重构的价值在于缩短参数列表,项目中常见作用于将很长的网络请求参数封装在一个对象中,然后直接将对象转成 JSON格式请求。
关于本书每章节的思维导图和书籍电子版,已上传至github。如有误,请指正!
详情参考:
https://github.com/CoralXss/AndroidFrameworkProcessChart [ 重构-坏代码味道.xmind ]