代码坏味(Bad Smell、Code Smell)
神秘命名(Mysterious Name)
-
说明:我们应该深思熟虑如何给函数、 模块、 变量和类命名, 使它们能清晰地表明自己的功能和用法。
-
措施:可参考7. 命名约定 — Google 开源项目风格指南 (zh-google-styleguide.readthedocs.io)
重复的代码(Duplicated Code)
- 说明: 一旦有重复代码存在, 阅读这些重复的代码时你就必须加倍仔细, 留意其间细微的差异。
过长的方法(Long Method)
- 说明:函数越长, 就越难理解。
过长的参数列表(Long Parameter List)
全局数据(Global Data)
- 说明:全局数据的问题在于, 从代码库的任何一个角落都可以修改它, 而且没有任何机制可以探测出到底哪段代码做出了修改。 全局数据最显而易见的形式就是全局变量, 但类变量和单例(singleton) 也有这样的问题。
可变数据(Mutable Data)
- 说明:对数据的修改经常导致出乎意料的结果和难以发现的bug。
发散式变化(Divergent Change)
- 说明:如果某个模块经常因为不同的原因在不同的方向上发生变化, 发散式变化就出现了。 当你看着一个类说: “呃, 如果新加入一个数据库, 我必须修改这3个函数; 如果新出现一种金融工具, 我必须修改这4个函数。 ”这就是发散式变化的征兆。
霰弹式变化(Shotgun Surgery)
- 说明:霰弹式修改类似于发散式变化, 但又恰恰相反。 如果每遇到某种变化, 你都必须在许多不同的类内做出许多小修改, 你所面临的坏味道就是霰弹式修改。 如果需要修改的代码散布四处, 你不但很难找到它们, 也很容易错过某个重要的修改。
依恋情结(Feature Envy)
- 所谓模块化, 就是力求将代码分出区域, 最大化区域内部的交互、 最小化跨区域的交互。 但有时你会发现, 一个函数跟另一个模块中的函数或者数据交流格外频繁, 远胜于在自己所处模块内部的交流, 这就是依恋情结的典型情况。
数据泥团(Data Clumps)
- 说明: 你常常可以在很多地方看到相同的三四项数据: 两个类中相同的字段、 许多函数签名中相同的参数。 这些总是绑在一起出现的数据真应该拥有属于它们自己的对象。
- 距离
基本类型偏执(Primitive Obsession)
- 举例:大多数编程环境都大量使用基本类型, 即整数、 浮点数和字符串等。很多程序员不愿意创建对自己的问题域有用的基本类型, 如钱、 坐标、 范围、日期、手机号等。 于是, 我们看到了把钱当作普通数字来计算的情况、 计算物理量时把英寸与毫米相加的情况 。字符串是这种坏味道的最佳培养皿 。
重复的Switch语句(Repeated Switches)
- 说明:重复的switch的问题在于: 每当你想增加一个选择分支时, 必须找到所有的switch, 并逐一更新。 程序员们太过于忽视多态的价值。任何switch语句、所有条件逻辑都应该考虑用多态取代。
循环语句(Loops)
- 说明:管道操作(如filter和map) 可以帮助我们更快地看清被处理的元素以及处理它们的动作 。
- 举例:JS中的filter、map
// c++20 for循环支持管道
#include <iostream>
#include <ranges>
int main()
{
auto const num = { 0,1,2,3,4,5 };
auto even = [](int i) { return 0 == i % 2; };
auto square = [](int i) { return i * i; };
for (int i : num | std::views::filter(even) | std::views::transform(square)) {
std::cout << i << ' ';
}
}
冗赘的元素(Lazy Element)
- 说明:可能有这样一个函数, 它的名字就跟实现代码看起来一模一样;也可能有这样一个类,但随着重构的进行越变越小 ,只剩了一个简单的函数。通常你只需要使用内联函数或是内联类 。 如果这个类处于一个继承体系中, 可以使用折叠继承体系 。
夸夸其谈通用性(Speculative Generality)
- 说明:当有人说“噢, 我想我们总有一天需要做这事”, 并因而企图以各式各样的钩子和特殊情况来处理一些非必要的事情, 这种坏味道就出现了。 这么做的结果往往造成系统更难理解和维护。
临时字段(Temporary Field)
- 说明:有时你会看到这样的类: 其内部某个字段仅为某种特定情况而设。 这样的代码让人不易理解, 因为你通常认为对象在所有时候都需要它的所有字段。 在字段未被使用的情况下猜测当初设置它的目的, 会让你发疯。
过长的消息链(Message Chains)
- 说明:一旦对象间的关系发生任何变化, 客户端就不得不做出相应修改。
中间人(Middle Man)
- 说明:你也许会看到某个类的接口有一半的函数都委托给其他类, 这样就是过度运用。
内幕交易(Insider Trading)
- 说明:软件开发者喜欢在模块之间建起高墙, 极其反感在模块之间大量交换数据,因为这会增加模块间的耦合。 在实际情况里, 一定的数据交换不可避免, 但我们必须尽量减少这种情况, 并把这种交换都放到明面上来。如果两个模块有共同的兴趣, 可以尝试再新建一个模块, 把这些共用的数据放在一个管理良好的地方。继承常会造成密谋, 因为子类对超类的了解总是超过后者的主观愿望。
过大的类(Large Class)
异曲同工的类(Alternative Classes with Different interfaces)
纯数据类(Data Class)
- 说明:它们拥有一些字段, 以及用于访问(读写) 这些字段的函数, 除此之外一无长物。
被拒绝的遗赠(Refused Bequest)
- 说明:它们拥有一些字段, 以及用于访问(读写) 这些字段的函数, 除此之外一无长物。
注释(Commonts)
- 说明:一段代码有着长长的注释是因为代码很糟糕。当你感觉需要撰写注释时, 请先尝试重构, 试着让所有注释都变得多余

浙公网安备 33010602011771号