1 滥用控制语句
产生的原因:
和长函数产生的原因类似,平铺直叙地写代码,一点一点追加代码,没有遵循童子军军规,没有做到分离关注点。
坏味道:
- 嵌套的代码;
- else 语句;
- 重复的 switch;
- 循环语句。
存在的问题:
真正的问题在于它们会使代码变得复杂,超出人脑所能理解的范畴。
解决方案:
- 通过提取单个元素操作,降低循环语句的复杂度;
- 用卫语句来简化条件表达式的编写,降低选择语句的复杂度;
- 对于重复的 switch 本质上是缺少了一个模型,可以使用多态取代条件表达式,引入缺少的模型,消除重复的 switch。
一个衡量代码复杂度的标准是圈复杂度,我们可以通过工具检查一段代码的圈复杂度。
编程规则:
- 函数至多有一层缩进;
- 不要使用else关键字。
2 与封装有关的坏味道:过长的消息链和基本类型偏执
坏味道:
- 过长的消息链,或者叫火车残骸——一行代码中是否出现了连续的方法调用
- 基本类型偏执。
解决方案:
-
火车残骸的代码就是连续的函数调用,它反映的问题就是把实现细节暴露了出去,缺乏应有的封装,而产生了耦合。
重构的手法是隐藏委托关系,实际就是做封装,做到解耦。编程指导原则:迪米特法则。 -
基本类型偏执就是用各种基本类型作为模型到处传递,这种情况下通常是缺少了一个模型。
解决它,常用的重构手法是以对象取代基本类型,也就是提供一个模型代替原来的基本类型。
基本类型偏执不局限于基本类型,字符串也是这种坏味道产生的重要原因,再延伸一点,集合类型也是。编程规则:封装所有的基本类型和字符串。
请记住一句话:
构建模型,封装散落的代码。
坏味道之火车残骸示例:
String name = book.getAuthor().getName();
注意,当你必须要首先了解一个类的细节,才能写出代码时,这只能说明一件事,这个封装是失败的。
我们很多程序员对封装的理解一直停留在数据结构加算法的层面,这使暴露细节成了常态,而封装反倒成了稀缺。而要想摆脱初级程序员的水平,就要先从少暴露细节开始。
重构之后:
class Book {
...
public String getAuthorName() {
return this.author.getName();
}
...
}
String name = book.getAuthorName();
坏味道之基本类型偏执示例:
public double getDisplayPrice() {
BigDecimal decimal = new BigDecimal(this.price);
return decimal.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
重构之后:
class Price {
private long price;
public Price(final double price) {
if (price <= 0) {
throw new IllegalArgumentException("Price should be positive");
}
this.price = price;
}
}
一旦有了这个模型,我们还可以再进一步,比如,如果我们想要让价格在对外呈现时只有两位,在没有 Price 类的时候,这样的逻辑就会散落代码的各处,事实上,代码里很多重复的逻辑就是这样产生的。而现在我们可以在 Price 类里提供一个函数:
public double getDisplayPrice() {
BigDecimal decimal = new BigDecimal(this.price);
return decimal.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
网友评论