类中的异味
可度量的异味
注释
症状
- 代码中出现注释
原因
作者认为某些内容没有说清楚
- 采用某种做法的原因
- 某种复杂的算法的实现
措施
收益
可以增强表述能力,有可能暴露出重复性
例外
有些注释是必要的,比如需求文档的链接
过长的方法
症状
- 代码块行数很多(个人感觉超过屏幕的高度为多)
原因
没有及时分解代码块,只是一味地在原来的位置增加代码
措施
- 根据代码块的注释和空行和空格,分析前后语义,抽取方法。
- 找到一些重构技术,用于清理直线式代码( 即大量代码都放在一行上) 、条件式和变量
收益
可以增强表述能力,有可能暴露出重复性,通常有助于建立新的类和抽象。
例外
有些复杂的算法需求,天然需求很多行
过大的类
症状
- 代码行数很多
- 大量实例变量
- 大量方法
原因
没有及时根据职责拆分类,只是一味地在原来的类上增加代码。
措施
收益
可以增强表述能力,有可能暴露出重复性
例外
无
过长的参数表
症状
- 方法有1个或2个以上的参数(个人觉得3个以上)
原因
可能是为了尽量减少对象之间的耦合。这样做不是由被调用对象来了解类之间的关系,而是让调用者来确定所有一切。
也有可能是程序员对例程进行了通用化
措施
收益
可以增强表述能力,有可能暴露出重复性。通常可以缩小规模。
例外
- 不希望两个类出现依赖
- 参数不存在有意义的分组
命名
名字中包含类型
症状
- 名字采用复合词,包含类型信息
- 匈牙利记法,如iCount
- 变量名反映的是类型,而不是其用途
原因
- 没有ide的时代,命令包含类型增加了可读性
措施
- 重命名方法
- 重命名字段
收益
可以增强表述能力,有可能暴露出重复性
例外
有些场景命名中的类型是有帮助的,如sql的字段
表达力差的名字
症状
- 单字符或双字符
- 无元音的名字
- 带编号的变量,如panel1,panel2
- 奇怪的缩写
- 容易误导的名称
原因
取名没有规范,过于随意
措施
- 重命名方法
- 重命名字段
收益
可以增强表述能力
例外
- i,j,k等循环变量
- 只在几行代码中起作用的单字符变量
- 函数的多个版本实现
不一致的名字
症状
- 同一个对象却有多个名字
原因
不同的人会在不同时刻创建类, 但作用是相同的, 导致名称不一样
措施
- 重命名方法
- 重命名字段
收益
可以增强表述能力, 有可能暴露出重复性
例外
无
不必要的复杂性
死代码
症状
- 变量、参数、字段、代码段、方法或类未在任何地方使用(除了测试)
原因
- 需求变更, 或采用了新方法, 未充分清理
- 简化代码时未发现冗余的逻辑
措施
- 删除相关代码并测试
收益
降低规模。可以增强表述能力,代码更简单。
例外
- 为子类或者调用方提供的hook等方法
过分一般性
症状
- 未使用的变量、参数、字段、代码段、方法或类; 没有实际作用的继承体系和调用链
- 就当前需求来说, 代码实现过于复杂
原因
- 预先设计, 但是实际需求并没有用到该设计, 或者不符合该设计
措施
收益
降低规模。可以增强表述能力,代码更简单。
例外
- 框架代码
- 为测试提供的接口
重复
魔法数
症状
- 代码里出现了数值常量或者字符串常量
原因
- 第一次添加常量时, 错误地认为该常量只会在该处使用
措施
- 特殊值: 以符号常量取代魔法数
- 可根据规则生成: 替换为函数
收益
减少重复。可以增强表述能力,代码更简单。
例外
- 为了提高测试代码的可读性
重复代码
症状
- 简单形式: 代码实现几乎相同
- 复杂形式: 代码作用几乎相同
原因
- 不同的程序员独立开发产生
- 懒惰, 常见于不愿意理解原代码的逻辑, 直接复制原代码少量修改
措施
收益
减少重复, 降低规模,代码更简单。
例外
- 重复代码可读性更好
- 只是恰好实现相同, 代码的作用并没有什么关系
具有不同接口的相似类
症状
- 两个类的作用似乎相同, 但是使用了不同的方法名
原因
- 开发代码时, 没有注意到已有类似代码
措施
协调各个类, 使他们一致, 从而去除其中一个
收益
减少重复, 降低规模,可能增强表述能力。
例外
- 有时这些类无法修改, 比如在不同的库中
条件逻辑
Null检查
症状
- 反复出现Null检查代码
原因
- 没有正确初始化对象和设置默认值
措施
- 采用默认值
- 引入Null对象
收益
减少重复, 减少逻辑错误。
例外
- 只出现一次的Null检查
- Null对象的方法必须实现安全且符合逻辑的行为
- Null有多种含义
复杂的布尔表达式
症状
- 复杂的and, or, not表达式
原因
- 复杂的业务逻辑
- 逻辑多次修改叠加
措施
- DeMorgan法则化简逻辑
- 引入解释变量
- 以卫语句取代嵌套条件式, 提前剔除某些条件
- 分解条件表达式将各个部分置于它自己的方法中。
收益
可能增强表述能力
例外
- 某些本质上复杂的逻辑, 改善不大
特殊用例
症状
- 复杂的if语句
- 在工作前某些特定值的检查
原因
没有对要判断的对象进行很好的分析和抽象
措施
- 条件式替换为多态
- 如果过个if子句的内容类似, 修改语句使之适用于多种情况(用变量控制不同的部分)
收益
可以增强表述能力, 可能暴露重复性问题
例外
- 递归代码的退出判断
- 有时if子句反而是最简单的
模拟继承(switch语句)
症状
- switch语句
- 多条if, elif
- instanceof类型判断
原因
懒得引入类型
措施
相同条件的switch语句多处出现
- 抽取方法。抽出每个分支的代码
- 搬移方法。将相关代码搬移至适当的类
- 以子类取代类型码或以状态/策略取代类型码。建立继承体系结构
- 以多态取代条件表达式, 去除条件式
如果条件式出现在一个单独的类中,可以通过将参数替换为显式方法或引入Null对象来取代条件逻辑。
收益
可以增强表述能力, 可能暴露重复性问题
例外
- 仅出现一次的switch
- 工厂方法的switch
类之间的异味
数据
基本类型困扰
症状
- 使用了基本类型或接近基本类型的类型(int,float,String,等等)
- 存在表示小整数的常量和枚举
- 存在表示字段名的字符串常量
原因
当ArrayList(或其他一些通用结构)被滥用时,会出现这类紧密相关问题。
- 没有使用类
- 模拟类型
- 模拟字段访问函数
措施
对于缺失对象
- 参见的”数据泥团”,解决数据泥团问题后通常可以封装基本类型。
- 通过以对象取代数据值,建立首类(first-class)数据值。
对于模拟类型,一个整型类型码对应一个类
- 对于该类型码,如果行为没有条件,则它更像是一个枚举,因此可以以类取代类型码。
- 如果此类型码是不可变的,而且该类还没有子类,则将以子类取代类型码。
- 如果类型码改变,或者类已经有子类了,就以状态/策略取代类型码
对于模拟字段访问函数
- 如果基本类型专门用于处理某些数组元素,以对象取代数组。
收益
可以增强表述能力, 可能暴露重复性问题, 通常能表明还需要使用其他重构技术。
例外
- 对象替换基本类型, 会增加开销
数据类
症状
- 类只包含数据成员
原因
类还未重复开发, 还没有抽象出行为
措施
- 使用封装字段防止直接访问字段
- 对方法尽可能采用移除设值方法
- 使用封装集合防止直接访问任何集合类型的字段。
- 査看对象的各个客户。可以对客户使用抽取方法,取出与类相关的代码,然后釆用搬移方法将其置于类中。
- 在完成上述工作后,你可能会发现类中还有多个相似的方法。使用重命名方法、抽取方法、添加参数或移除参数等重构技术来协调签名并消除重复。
- 对字段的大多数访问都不再需要了,因为搬移的方法涵盖了实际使用。此时便可以使用隐藏方法来消除对获取方法和设置方法的访问
收益
可以增强表述能力,可能会暴露重复性问题
例外
- 封装字段对性能有影响
数据泥团
症状
- 两到三个同样的项频繁地一同出现在类和参数表中。
- 在类中同时存在多组字段和方法
- 各组字段名以类似的子字符串开头或结尾
原因
这些字段和方法, 往往应该属于另一个类, 但是没有人发现类缺失
措施
- 是类的字段: 抽取字段
- 在方法签名中: [引入参数对象](Introduce #Parameter-Object:引入参数对象-, 保持参数对象完整
- 查看调用, 利用搬移方法等重构方法
收益
可以增强表述能力,可能会暴露重复性问题, 通常会降低规模
例外
- 传递对象会带来额外的依赖性
临时字段
症状
- 字段仅在某些时候进行设置,而在其余时间为Null(或不使用)
原因
通过字段而不是参数来传递信息
措施
- 抽取类, 移出字段及相关代码
收益
增强了表述能力并提高了清晰性。可能会减少重复,特别是在其他位置可以使用新类时。
例外
- 有时新对象不是一个有用的对象
继承
拒收的遗赠
症状
- 坦率的拒绝: 一个类继承自其父类,但是抛出了一个异常而不是支持一个方法
- 隐式的拒绝: 某个继承的方法不能正常工作
- 客户试图通过子类的句柄而不是父类的句柄来访问类。
- 继承没有什么实际意义,子类并不是父类的一个例子。
原因
某个类之所以继承自另一个类,可能只是为了实现方便,而不是真的想用这个类来取代其父类。
措施
- 如果不会导致混淆,可以不去管它。
- 如果找不到共享某个类关系的理由,就以委托取代继承
- 如果父子关系确实有意义,则可以通过抽取子类、下移字段和下移方法来创建一个新的子类让这个类拥有非拒绝行为,并将父类的客户修改为这个新类的客户
收益
可增强表述能力,能改善可测试性
例外
- 有时拒收的遗赠可用于避免新类型的爆炸(通过拒绝特定的方法,就不需要为各种拒绝组合创建一种类型继承体系了)
不当的紧密性
症状
- 一个类访问了它父类的内部(本应是私有的)部分
原因
子类过分依赖了父类的一些信息, 过度耦合
措施
收益
可以减少重复。通常能够增强表述能力,还可能会降低规模。
例外
无
懒惰类
症状
- 类没有做什么工作,似乎是它的父类、子类或者调用者完成了所有相关工作
原因
- 过度设计
- 重构过程中,类的所有职责都移到了其他位置
措施
收益
可降低规模。能增强表述能力,代码更简单
例外
- 有时使用懒惰类是为了传达意图
职责
依恋情节
症状
- 一个方法似乎过于强调处理其他类的数据,而不是它自己的数据
原因
常见的现象, 一般是因为代码的迭代, 类的职责发生了便宜
措施
- 使用搬移方法将动作放在适当的类中
收益
可减少重复。通常会增强表述能力,暴露需重构的问题
例外
- 策略模式或者访问者模式
说明
区分依恋情结和不当的紧密性有时并不简单。
依恋情结是指,一个类自身所做甚少,需要借助大量其他类才能完成自己的工作。
不当的紧密性则是指,一个类为了访问某些本不该访问的内容,过于深入到其他类中。
不当的紧密性(一般形式)
症状
- 一个类访问了另一个类的内部部分(而这一部分本应是私有的)
原因
两个类之间有时可能会稍有关联。等你意识到存在问题时,这两个类已经过于耦合了
措施
- 如果两个独立的类彼此“纠缠”,则使用搬移方法和搬移字段将适当的部分放在适当的类中。
- 如果纠缠的部分看上去是一个被遗漏的类,则使用抽取类和隐藏委托引入这个新类。
- 如果类相互指向对方,则使用将将双向关联改为单向
- 如果子类与父类过于耦合, 参考“不当的紧密性(子类形式)”
收益
可以减少重复。通常能够增强表述能力,还可能会降低规模。
例外
无
消息链
症状
- a.b().c().d()形式的调用
原因
一个对象必须与其他对象协作才能完成工作,这是自然,问题在于这不仅会使对象相互耦合,还会使获得这些对象的路径存在耦合。
方法不应与“陌生人”说话,也就是说,它应当只与其自身、其参数、它自己的字段或者它创建的对象有信息传递。
措施
- 如果处理实际针对的是目标对象(即消息链最末端的对象),则使用抽取方法和搬移方法将处理置于该对象中。
- 使用隐藏委托使方法仅依赖于一个对象(因此应将d()方法置于a对象中。这可能还需要为b()和c()对象增加一个d()方法)。
收益
可以减少重复或者暴露重复性问题
例外
如果过分应用隐藏委托,那么对象都去忙着委托,就会没有一个真正做实事的
中间人
症状
- 类的大多数方法都是在调用另一个对象的同一个(或类似的)方法
- 如果一个类的任务主要是将工作委托出去,这样的类就称为中间人
原因
可能是因为应用了隐藏委托来解决消息链引起的。
可能在此之后其他一些特性已经被移除了,剩下的主要就是委托方法了。
措施
收益
可降低规模,还可能增强表述能力
例外
- 有些模式[如代理模式(Proxy)和装饰器模式(Decorator)]会有意地创建委托
- 要在中间人和消息链之间权衡。
相关改变
发散式改变
症状
- 尽管每次原因各异,但发现自己所修改的是同一个类
原因
类在发展过程中会承担越来越多的职责,但没有人注意到这会涉及两种截然不同的决策
措施
- 如果类既要找到对象,又要对其做一些处理,则让调用者査找对象并将该对象传入,或者让类返回调用者使用的值。
- 釆用抽取类为不同的决策抽取不同的类。
- 如果几个类共享相同类型的决策,则可以考虑合并这些新类(例如,通过抽取超类或抽取子类)
收益
可增强表述能力(更好地传达意图),还可以提高健壮性以备将来修改。
例外
无
霰弹式修改
症状
- 仅做一个简单的修改就要更改多个类。
原因
- 将多个类分离是代码的一大职责。你可能缺失一个通晓全部职责的类(而大量修改本应通过这个类来完成)。
- 也有可能是因为你过度去除发散式改变而导致了这个异味。
措施
收益
可以减少重复,增强表达能力,并能改进可维护性(将来的修改将更为本地化)。
例外
无
并行继承体系
症状
- 在一个继承体系中建立了一个新子类后,却发现还需要在另一个继承体系中创建一个相关的子类。
- 可能发现两个继承体系中的子类有着相同的前缀(命名可以反映出协调继承体系的需求)。
- 这是霰弹式修改的一个特例
原因
这样两个类并不是无关的, 而是体现了同一决策的不同方面(维度)。
措施
- 使用搬移字段和搬移方法来重新分配特性,以便能去除某个继承体系。
收益
可以减少重复。可能会增强表述能力,也可能会降低规模
例外
无
组合爆炸
症状
- 你本来希望只引入一个单一的新类,但是最后却发现必须在继承体系的不同位置引入多个类。
- 你发现继承体系的各个层使用了一组常见的词(例如,一层增加了样式信息,下一层增加了可变性)
原因
这与并行继承体系相关,但是所有内容都折叠到了一个继承体系中 。
原本应当是独立的决策却通过一个继承体系实现了。
措施
- 如果问题不严重,可以釆用以委托取代继承, 通过为各种变化提供同样的接口,可以创建一个装饰器设计模式示例。
- 如果情况已经变得相当复杂了,则有必要考虑进行大型重构了,可能还需要梳理并分解继承体系
收益
可以减少重复, 降低规模
例外
无
类库
不完备的类库
症状
- 你正在使用一个库类,希望该类具有某种功能,但它没有。
原因
库类的作者未能满足你的要求
措施
收益
可以减少重复
例外
如果有多个项目,每个项目都使用不兼容的方式来扩展一个类,那么在改变库时就会导致额外的工作
重构技法
Composing Methods:优化函数的构成
Extract Method:抽取方法
before1
2
3
4
5
6
7void printOwing() {
printBanner();
// Print details.
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
after1
2
3
4
5
6
7
8
9void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
Inline Method:内联方法
before1
2
3
4
5
6
7
8
9class PizzaDelivery {
// ...
int getRating() {
return moreThanFiveLateDeliveries() ? 2 : 1;
}
boolean moreThanFiveLateDeliveries() {
return numberOfLateDeliveries > 5;
}
}
after1
2
3
4
5
6class PizzaDelivery {
// ...
int getRating() {
return numberOfLateDeliveries > 5 ? 2 : 1;
}
}
Extract Variable:提炼变量
before1
2
3
4
5
6
7
8void renderBanner() {
if ((platform.toUpperCase().indexOf("MAC") > -1) &&
(browser.toUpperCase().indexOf("IE") > -1) &&
wasInitialized() && resize > 0 )
{
// do something
}
}
after1
2
3
4
5
6
7
8
9void renderBanner() {
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIE = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIE && wasInitialized() && wasResized) {
// do something
}
}
Inline Temp:内联临时变量
before1
2
3
4boolean hasDiscount(Order order) {
double basePrice = order.basePrice();
return basePrice > 1000;
}
after1
2
3boolean hasDiscount(Order order) {
return order.basePrice() > 1000;
}
Replace Temp with Query:以查询取代临时变量
before1
2
3
4
5
6
7
8
9double calculateTotal() {
double basePrice = quantity * itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
}
else {
return basePrice * 0.98;
}
}
after1
2
3
4
5
6
7
8
9
10
11double calculateTotal() {
if (basePrice() > 1000) {
return basePrice() * 0.95;
}
else {
return basePrice() * 0.98;
}
}
double basePrice() {
return quantity * itemPrice;
}
Split Temporary Variable:拆分临时变量
before1
2
3
4double temp = 2 * (height + width);
System.out.println(temp);
temp = height * width;
System.out.println(temp);
after1
2
3
4final double perimeter = 2 * (height + width);
System.out.println(perimeter);
final double area = height * width;
System.out.println(area);
Remove Assignments to Parameters:移除参数赋值
before1
2
3
4
5
6int discount(int inputVal, int quantity) {
if (inputVal > 50) {
inputVal -= 2;
}
// ...
}
after1
2
3
4
5
6
7int discount(int inputVal, int quantity) {
int result = inputVal;
if (inputVal > 50) {
result -= 2;
}
// ...
}
Replace Method with Method Object:以方法对象替代方法
before1
2
3
4
5
6
7
8
9class Order {
// ...
public double price() {
double primaryBasePrice;
double secondaryBasePrice;
double tertiaryBasePrice;
// Perform long computation.
}
}
after1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Order {
// ...
public double price() {
return new PriceCalculator(this).compute();
}
}
class PriceCalculator {
private double primaryBasePrice;
private double secondaryBasePrice;
private double tertiaryBasePrice;
public PriceCalculator(Order order) {
// Copy relevant information from the
// order object.
}
public double compute() {
// Perform long computation.
}
}
Substitute Algorithm:替换算法
before1
2
3
4
5
6
7
8
9
10
11
12
13
14String 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 "";
}
after1
2
3
4
5
6
7
8
9
10String 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 "";
}
Moving Features between Objects:在对象之间搬移特性
Move Method:搬移方法
Move Field:搬移字段
Extract Class:抽取类
Inline Class:内联类
Hide Delegate:隐藏委托
Remove Middle Man:移除中间人
Introduce Foreign Method:引入外加函数
before1
2
3
4
5
6
7
8class Report {
// ...
void sendReport() {
Date nextDay = new Date(previousEnd.getYear(),
previousEnd.getMonth(), previousEnd.getDate() + 1);
// ...
}
}
after1
2
3
4
5
6
7
8
9
10class Report {
// ...
void sendReport() {
Date newStart = nextDay(previousEnd);
// ...
}
private static Date nextDay(Date arg) {
return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
}
}
Introduce Local Extension:引入本地扩展
Organizing Data:重新组织数据
Change Value to Reference:将值对象改为引用对象
Change Reference to Value:将引用对象改为值对象
Duplicate Observed Data:复制被监视数据
Self Encapsulate Field:自封装字段
before1
2
3
4
5
6class Range {
private int low, high;
boolean includes(int arg) {
return arg >= low && arg <= high;
}
}
after1
2
3
4
5
6
7
8
9
10
11
12class Range {
private int low, high;
boolean includes(int arg) {
return arg >= getLow() && arg <= getHigh();
}
int getLow() {
return low;
}
int getHigh() {
return high;
}
}
Replace Data Value with Object:以对象取代数据值
Replace Array with Object:以对象取代数组
before1
2
3String[] row = new String[2];
row[0] = "Liverpool";
row[1] = "15";
after1
2
3Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");
Change Unidirectional Association to Bidirectional:将单向关联改为双向
Change Bidirectional Association to Unidirectional:将双向关联改为单向
Encapsulate Field:封装字段
before1
2
3class Person {
public String name;
}
after1
2
3
4
5
6
7
8
9
10class Person {
private String name;
public String getName() {
return name;
}
public void setName(String arg) {
name = arg;
}
}
Encapsulate Collection:封装集合
Replace Magic Number with Symbolic Constant:以符号常量取代魔法数
before1
2
3double potentialEnergy(double mass, double height) {
return mass * height * 9.81;
}
after1
2
3
4
5static final double GRAVITATIONAL_CONSTANT = 9.81;
double potentialEnergy(double mass, double height) {
return mass * height * GRAVITATIONAL_CONSTANT;
}
Replace Type Code with Class:以类取代类型码
Replace Type Code with Subclasses:以子类取代类型码
Replace Type Code with State/Strategy:以状态/策略取代类型码
Replace Subclass with Fields:以字段取代子类
Simplifying Conditional Expressions:简化条件表达式
Consolidate Conditional Expression:合并条件表达式
before1
2
3
4
5
6
7
8
9
10
11
12
13double disabilityAmount() {
if (seniority < 2) {
return 0;
}
if (monthsDisabled > 12) {
return 0;
}
if (isPartTime) {
return 0;
}
// Compute the disability amount.
// ...
}
after1
2
3
4
5
6
7double disabilityAmount() {
if (isNotEligibleForDisability()) {
return 0;
}
// Compute the disability amount.
// ...
}
Consolidate Duplicate Conditional Fragments:合并重复条件片断
before1
2
3
4
5
6
7
8if (isSpecialDeal()) {
total = price * 0.95;
send();
}
else {
total = price * 0.98;
send();
}
after1
2
3
4
5
6
7if (isSpecialDeal()) {
total = price * 0.95;
}
else {
total = price * 0.98;
}
send();
Decompose Conditional:分解条件表达式
before1
2
3
4
5
6if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * winterRate + winterServiceCharge;
}
else {
charge = quantity * summerRate;
}
after1
2
3
4
5
6if (isSummer(date)) {
charge = summerCharge(quantity);
}
else {
charge = winterCharge(quantity);
}
Replace Conditional with Polymorphism:以多态取代条件表达式
before1
2
3
4
5
6
7
8
9
10
11
12
13
14class Bird {
// ...
double getSpeed() {
switch (type) {
case EUROPEAN:
return getBaseSpeed();
case AFRICAN:
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
case NORWEGIAN_BLUE:
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
throw new RuntimeException("Should be unreachable");
}
}
after1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23abstract class Bird {
// ...
abstract double getSpeed();
}
class European extends Bird {
double getSpeed() {
return getBaseSpeed();
}
}
class African extends Bird {
double getSpeed() {
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
}
}
class NorwegianBlue extends Bird {
double getSpeed() {
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
}
// Somewhere in client code
speed = bird.getSpeed();
Remove Control Flag:移除控制标记
before1
2
3
4
5
6
7
8
9
10
11
12String foundMiscreant(String[] people){
String found = "";
for (int i = 0; i < people.length; i++) {
if (found.equals("")) {
if (people[i].equals ("John")){
sendAlert();
found = "John";
}
}
}
return found;
}
after1
2
3
4
5
6
7
8
9
10
11String foundMiscreant(String[] people){
for (int i = 0; i < people.length; i++) {
if (found.equals("")) {
if (people[i].equals ("John")){
sendAlert();
return "John";
}
}
}
return "";
}
Replace Nested Conditional with Guard Clauses:以卫语句取代嵌套条件式
before1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public double getPayAmount() {
double result;
if (isDead){
result = deadAmount();
}
else {
if (isSeparated){
result = separatedAmount();
}
else {
if (isRetired){
result = retiredAmount();
}
else{
result = normalPayAmount();
}
}
}
return result;
}
after1
2
3
4
5
6
7
8
9
10
11
12public double getPayAmount() {
if (isDead){
return deadAmount();
}
if (isSeparated){
return separatedAmount();
}
if (isRetired){
return retiredAmount();
}
return normalPayAmount();
}
Introduce Null Object:引入Null对象
before1
2
3
4
5
6if (customer == null) {
plan = BillingPlan.basic();
}
else {
plan = customer.getPlan();
}
after1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class NullCustomer extends Customer {
boolean isNull() {
return true;
}
Plan getPlan() {
return new NullPlan();
}
// Some other NULL functionality.
}
// Replace null values with Null-object.
customer = (order.customer != null) ?
order.customer : new NullCustomer();
// Use Null-object as if it's normal subclass.
plan = customer.getPlan();
Introduce Assertion:引入断言
before1
2
3
4
5
6
7double getExpenseLimit() {
// Should have either expense limit or
// a primary project.
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit :
primaryProject.getMemberExpenseLimit();
}
after1
2
3
4
5
6
7double getExpenseLimit() {
Assert.isTrue(expenseLimit != NULL_EXPENSE || primaryProject != null);
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit:
primaryProject.getMemberExpenseLimit();
}
Simplifying Method Calls:简化方法调用
Add Parameter:添加参数
Remove Parameter:移除参数
Rename Method:重命名方法
Separate Query from Modifier:将查询方法和修改方法分离
Parameterize Method:让方法携带参数
Introduce Parameter Object:引入参数对象
Preserve Whole Object:保持对象完整
before1
2
3int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);
after1
boolean withinPlan = plan.withinRange(daysTempRange);
Remove Setting Method:移除设值方法
Replace Parameter with Explicit Methods:以明确函数取代参数
before1
2
3
4
5
6
7
8
9
10
11void setValue(String name, int value) {
if (name.equals("height")) {
height = value;
return;
}
if (name.equals("width")) {
width = value;
return;
}
Assert.shouldNeverReachHere();
}
after1
2
3
4
5
6void setHeight(int arg) {
height = arg;
}
void setWidth(int arg) {
width = arg;
}
Replace Parameter with Method Call:以函数调用取代参数
before1
2
3
4int basePrice = quantity * itemPrice;
double seasonDiscount = this.getSeasonalDiscount();
double fees = this.getFees();
double finalPrice = discountedPrice(basePrice, seasonDiscount, fees);
after1
2int basePrice = quantity * itemPrice;
double finalPrice = discountedPrice(basePrice);
Hide Method:隐藏方法
Replace Constructor with Factory Method:以工厂方法取代构造函数
before1
2
3
4
5
6class Employee {
Employee(int type) {
this.type = type;
}
// ...
}
after1
2
3
4
5
6
7
8class Employee {
static Employee create(int type) {
employee = new Employee(type);
// do some heavy lifting.
return employee;
}
// ...
}
Replace Error Code with Exception:以异常取代错误码
before1
2
3
4
5
6
7
8
9int withdraw(int amount) {
if (amount > _balance) {
return -1;
}
else {
balance -= amount;
return 0;
}
}
after1
2
3
4
5
6void withdraw(int amount) throws BalanceException {
if (amount > _balance) {
throw new BalanceException();
}
balance -= amount;
}
Replace Exception with Test:以测试取代异常
before1
2
3
4
5
6
7double getValueForPeriod(int periodNumber) {
try {
return values[periodNumber];
} catch (ArrayIndexOutOfBoundsException e) {
return 0;
}
}
after1
2
3
4
5
6double getValueForPeriod(int periodNumber) {
if (periodNumber >= values.length) {
return 0;
}
return values[periodNumber];
}
Pull Up Field:上移字段
Pull Up Method:上移方法
Pull Up Constructor Body:上移构造函数本体
before1
2
3
4
5
6
7
8class Manager extends Employee {
public Manager(String name, String id, int grade) {
this.name = name;
this.id = id;
this.grade = grade;
}
// ...
}
after1
2
3
4
5
6
7class Manager extends Employee {
public Manager(String name, String id, int grade) {
super(name, id);
this.grade = grade;
}
// ...
}
Dealing with Generalization:处理概括关系
Push Down Field:下移字段
Push Down Method:下移方法
Extract Subclass:抽取子类
Extract Superclass:抽取超类
Extract Interface:抽取接口
Collapse Hierarchy:折叠继承关系
Form Template Method:塑造模板函数
Replace Inheritance with Delegation:以委托取代继承
Replace Delegation with Inheritance:以继承取代委托