代码本应优雅!识别哪些需要重构的“坏味道”

​重构:对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

代码本应优雅!识别哪些代码中的“坏味道”是重构的起点。

Duplicated Code:重复代码

重复代码是首当其冲的“坏味道”,也是最易识别的重构点:相同或相似的代码片段出现两次或以上就可以称之为重复代码了。重复代码的出现不单单是重复劳动(即使是拷贝),更为关键的问题是当重复代码需要变更时可能会导致修改遗漏,极大降低代码的可扩展性和可维护性。

Long Method:长函数

没有具体的标准判定一个函数内的代码行数超过哪个具体数值才能算的上“过长”,但是,如果我们去阅读某个函数的源码,其代码行数过多已经造成理解的混乱或障碍,就已经嗅到了函数过长引起的坏味道了。

Large Class:过大的类

单个类具有庞大的代码片段,拥有众多的属性和方法,承担了过多的事情,一般会违反SRP原则,或者抽象层级表述不清。

Long Parameter List:过长的函数参数列表

参数个数一般不建议超过三个。过多参数导致函数职责难以理解,引起可维护性的下降。如果参数等于或大于三个我们就可以考虑进行适当的重构了。

Divergent Change:发散式变化

如果某个类经常由于不同的原因在不同方向上发生变化,这种坏味道就是“发散式变化”,本质上来看,对于这种类我们要关注其是否遵循了单一职责原则。

Shotgun Surgery:霰弹式修改

这种坏味道就是:当遇到某种变化,需要在代码中许多不同的地方进行修改。

Feature Envy

对象封装了数据和行为,如果一个类中的函数对其他类中数据的兴趣超过了自己所在的类,例如,某个函数进行运算要频繁的从获取其他类中的数据,那么这种情况就需要考虑函数的位置是否应该从该类中移出到其他类中。

Data Clumps

如果在代码中常常可以看到在许多地方看到相同的三四项数据,比如勒种有相同的字段,或函数签名中的相同的参数,那么这些经常出现的、相同的数据项应该为之分配一个类了。

Primitive Obsession

不论是在类的属性或方法参数中,你可能更倾向于使用语言自带的简单数据类型。但有时候,不应该偏执于使用基本类型,而应该尝试将这些属性或参数封装为更具表现力的类。比如常见的地址信息、钱(数量+货币单位)、姓名信息(姓 + 名)等等。

Switch 语句

Switch语句或if/else条件分支的使用具备一定的可读性和简洁性,其本身并不具有坏味道。这些语句的使用通常意味着要进行分支决策,问题在于这种同样的决策逻辑往往会出现在系统的不同地方而非只有一处。当需要扩展时则需要修改代码分支,且需要多处修改不能遗漏,这就是“坏味道”

Lazy Class

类不应该被过度创建,已经创建的类要具备其应有的职责和价值。代码中不应该存在这种没有太多实际价值,且还需要额外维护的类。

Speculative Generality

代码中规划了太多的 “TODO”了,但这些“TODO”工作短期内不会做或者后续可能根本不会进行实现,应该避免这些“过度的规划”。

Temporary Field

如果类中的某个或少数几个变量都是为了某种特定情况而存在,其实在大多数情况下都不会用到这些属性。当阅读代码时不由自主的猜测这些属性设立的最初目的,对你来说是一种干扰。

Message Chains

如果在方法a()如果存在类似这样的代码:

B b = b.call();

C c = c.call(b);

D d = d.call(c);

......

调用多个方法,又将返回对象作为参数依次调用下一个方法。这种显式的调用链导致了A.a()中的代码与导航结构紧密耦合,当对象关系发生变化时不得不对A进行代码修改。

Middle Man

封装是对象的关键特征之一,将复杂的操作进行封装,对外屏蔽操作的细节,调用方将实现逻辑委托给其他类或方法实现。但如果你的类中存在过多的方法都是基于这种“委派”就需要引起注意,这是一种 “过度使用” 了。

Inappropriate Intimacy

是指两个类的关系过于密切,类中的方法过多的关注彼此的数据,导致两个类间紧密的耦合和交互的错综复杂。这种情况下,需要考虑是否应该将方法或属性进行移动,尽量使其在单个类中内聚。

Alternative Classes with Different Interfaces

这种现象是指:如果两个函数都做的是同样的事情,但在代码中却有不同的方法签名。Comments

注释

本身没有问题,合理的和必要注释是有价值的。但问题在于不要出现无效或不合理的注释,比如:

  1. 不恰当的信息:版本/变更历史记录/作者等适合通过配置管理系统维护的不要放在注释中
  2. 废弃的注释:建议删除废弃的或过期的注释
  3. 冗余的注释:代码具有自注释能力,去除无用的冗余的注释

输出参数:

函数的形参传递引用,在函数内部对形参指向的数据直接进行修改,这种隐蔽的修改就是一种坏味道,造成程序的可读性下降,出现Bug时不利于问题排查。如果确实需要修改,则应该是通过返回值的方式实现。

标识参数

如果函数的参数有明显的布尔值参数或其他标识操作类型的变量就应该注意了,我们需要考虑函数是否承担了过多的职责,而非只专注于一件事儿。

死函数、死代码和注释掉的代码

所谓死函数就是永远不会被调用的函数,死代码是指永远不会被执行的代码,同注释掉的代码一样,这些死函数应该直接在代码中删除。

错误的抽象层级上的代码

这种坏味道是非常常见的问题,特别是那些又臭又长的函数,繁杂的代码中可能做了不同抽象层级需要做的事情。函数中应该只做一件事儿,且只做与函数所在的抽象层级一致的逻辑,不要把过多的下层逻辑代码都堆砌到上层。PS: 函数的命名就反映了你对函数所要表达的抽象层级。

垂直分割

变量和函数应该在靠近被使用的地方定义,不要相隔太远。对于被调用的函数而言,要尽量靠近调用方,方面阅读代码时,不要移动太多行数就能找到被调用函数(如果在一个类中)。

无处不在的魔数

代码中经常出现不能字描述的数字或字符串则称之为魔数,这种硬编码的数字对于维护来说简直是噩梦,所以必须要将其声明为常量后再引用。

代码本应优雅!识别哪些需要重构的“坏味道”

作为一个开发人员,除了实现功能之外,更为重要的是具备重构意识,经常思考如何才能更优雅的实现,而非机械的堆砌代码。


分享到:


相關文章: