代码的坏味道¶
-
神秘命名(Mysterious Name)
- 整洁的代码最终要的就是好的名字,所以我们会深思熟虑如何给函数、模块、变量和类命名,使它们能清晰的表明自己的功能和功法。
-
重复代码(Duplicated Code)
- 一旦重复代码的存在,阅读重复代码你就要加倍仔细,留意其差异。导致很容易出错。
-
过长函数(Long Function)
-
函数越长越难理解。
-
如何提炼:
- 寻找注释;
- 条件表达式;
- 循环;
-
-
过长参数列表(Long Parameter List)
-
过长的参数列表也会使人产生迷惑
-
如何解决
- 第一个参数可以获取第二个参数:去掉第二个参数;
- 正在从现有数据结构中抽出很多数据:保持原有对象;
- 有几个参数总是同时出现:合并这几个参数为一个对象;
- 多个函数拥有同样的参数:引入一个类;
-
-
全局数据(Global Data)
-
问题在于全局都可以修改,导致不知道谁修改了他
-
如何解决:
- 封装变量,进行控制和记录
-
-
可变数据(Mutable Data)
-
一个功能使用的变量,在另一处被改变了
-
如何解决:
- 封装变量
- 拆分变量
- 移动语句、提炼函数
- 查询和修改分离
- 已查询取代派生变量
- 函数组合成类、函数组合成变换
- 引用对象改为值对象
-
-
发散式变化(Divergent Change)
- 需要修改时,只需跳到系统的某一点,只在该处做修改,如果做不到这一点,那就很糟糕
- 如何解决:
- 拆分阶段
- 搬移函数
- 提炼函数
- 提炼类
-
散弹式修改(Shotgun Surgery)
- 如果每遇到某种变换,你都必须在许多不同的类内做出小的修改,你所面临的就是散弹式修改。很难找到修改的位置,也容易错过某个重要的修改。
- 如何解决:
- 搬移函数、搬移字段
- 组合函数成类
- 组合函数成变换
- 内联函数、内联类
-
依恋情节(Feature Envy)
- 一个函数跟另一个模块中的函数或者数据交流格外频繁,甚至远甚于本模块内部的交流,这就是依恋情结的典型案例。
- 如何解决
- 搬移函数
- 提炼函数、搬移函数
-
数据泥团(Data Clumps)
-
你常常可以在很多地方看到相同的三四项数据:两个类中的字段、函数等。
-
如何解决
- 提炼类、引入参数对象、保持对象完整;
-
-
基本类型偏执(Primitive Obsession)
-
大多数编程都适用基本类型,一些库会引入小对象,如日期。很对程序员不愿意创建自己的问题域有用的基本类型,如:钱、坐标、范围等。于是把钱作为一个普通的数字进行计算无视了其钱的其他属性
-
如何解决
- 以对象取代基本类型
- 以子类取代类型码
- 加上多态取代表达式
-
-
重复的switch(Repeated Switches)
- 任何switch语句都应该以多态取代条件表达式
-
循环语句(Loops)
-
鄙视循环语句
-
如何解决
- 管道取代循环
-
-
冗赘的元素(Lazy Element)
-
起初在编写的时候,程序员希望代码十分完善,功能强大但随着重构的进行只剩下一个函数了
-
如何解决
- 使用内联函数、内联类
- 折叠继承体系
-
-
夸夸其谈通用性(Speculative Generality)
-
如果用不到,那就不值得
-
如何解决:
-
内联函数、内联类
-
改变函数声明
-
-
-
临时字段(Temporary Field)
-
其内某个字段仅为某种特定情况而设。这样的代码不易理解,在代码未被使用的时候猜想他当初的目的,会使人发疯。
-
如何解决
- 提炼类、搬移函数
-
-
过长的消息链(Message Chains)
-
到你看到一个对象请求另一个对象,然后再请求另一个对象 ······ 这就是消息链。这就意味着强耦合。
-
如何解决:
- 隐藏委托关系,先观察消息链最终得到了什么看看是否可以提炼函数,在运用搬移函数把这个函数推入消息链。
-
-
中间人(Middle Man)
-
对象的基本特征就是封装,封装意味着委托。但是过度运用委托。你会看到某各类的接口有一半的函数都是委托给其他类。那这就是过度委托。
-
如何解决:
- 移除中间人、运用内联函数
-
-
内幕交易(Insider Trading)
-
模块之间大量交换数据,这会增加模块间的耦合。
-
如何解决:
- 搬移函数、搬移字段减少交流,建立新类管理;或者使用隐藏委托关系。
-
-
过大的类(Large Class)
-
一个类想做的事情太多了,往往就会出现好多字段。这样重复的代码就会接踵而至。
-
如何解决:
- 提炼超类、提炼子类
-
-
异曲同工的类(Alternative Classes with Different Interfaces)
-
使用类的好处是可以替换:今天使用这个类,未来可以换成另一个类。但只有两个类的接口一致时,才能做这种替换。
-
如何解决:
- 改变函数声明,反复使用搬移函数将某些行为移入类中,指导两者协议一致,重复代码使用提炼超类补偿一下;
-
-
纯数据类(Data Class)
-
它们拥有一些字段,以及用于访问读写这些字段的函数,除此一无是处。
-
如何解决:
- 封装记录、移除设值函数;
- 找出取值设值的位置,搬移函数、提炼函数
-
-
被拒绝的遗赠(Refused Bequest)
-
子类应该继承超类的函数和数据。戴氏他们不想或者不需要继承。
-
如何解决:
- 需要为这个子类新建一个兄弟类,在运用函数下移和字段下移把用不到的推给兄弟类
-
-
注释(Comments)
- 当你感觉撰写注释时,请先尝试重构,试着让所有的注释都变得多余;