重构-代码的坏味道(一)

808 阅读4分钟

你会看到

  • 神秘命名
  • 重复代码
  • 过长函数
  • 全局数据
  • 发散式变化
  • 霰弹式修改
  • 依恋情结
  • 数据泥团
  • 基本类型偏执
  • 重复的switch
  • 循环语句
  • 冗余的元素
  • 夸夸其谈通用性
  • 临时字段
  • 过长的消息链
  • 中间人
  • 过大的类
  • 内幕交易
  • 异曲同工的类
  • 纯数据类
  • 被拒绝的遗赠
  • 注释

神秘命名

修改命名可能是最常规的重构首发,包括修改函数声明,变量改名,字段改名等等

很多人并不愿意给程序元素改名,觉得不值得费这个时间,但好的名字能节省未来用在猜谜上的大把时间

改名并不仅仅是修改名字而已,如果你想不出一个好名字,说明背后很有可能潜藏着更深的设计问题。为了一个恼人的名字所付出的纠结,往往能够推动我们对代码进行精简

重复代码

如果你在一个以上的地点看到相同的代码结构,那么可以肯定:设法将他们合二为一

阅读这些重复的代码时你就必须加倍仔细,留意其间细微的差异,如果要修改就必须找出所有的副本来修改

过长代码

据我们的经验,活得最长,最好的程序,其中的函数都比较短

初次接触到这种代码库的程序员常常会觉得“计算都没有发生”——程序里满是无穷无尽的委托调用。

但和这样的程序共处几年后,你会发现小函数的价值所在。

间接性带来的好处——更好的诠释力,更易于分享,更多的选择——都是由小函数来支持的

固然小函数也会给代码的阅读者带来一些负担,因为你必须经常切换上下文,才能看明函数做了什么,但现代的开发环境能够在函数的调用处和声明处之间快速跳转,或是同时看到这两处。

不过说到底,让小函数易于理解的关键还是在于良好的命名

最终的效果是:你应该更积极的分解函数,

我们要遵循这样一条原则:每当感觉需要用注释来说明什么的时候,就应该把需要说明的东西写进一个单独的函数里并以其用途(而非实现手法)命名。我么可以对一组甚至短短一行代码做这样的事,哪怕替换后的函数调用动作比函数自身还要长,只要函数名称能够解释其用途,我们也该毫不犹豫那么做。关键不在于函数的长度,而在于函数“做什么”和“如何做”之间的语义距离

全局变量

它们是如何被来自地狱第四层的恶魔发明出来,胆敢使用它们的程序员如今在何处安息

全局变量的问题在于可以在代码的任何地方修改他们

而且没有任何机制可以侦测出到底哪段代码做出了修改,一次又一次,全局数据造成了哪些诡异的bug,而这些问题的根源却在遥远的别处,想要找出出错的代码难于登天

首要的防御手段是封装变量

// bad
const info = { name : "haha" }

// good
const info = { name : "haha" }
export function defaultGetInfo(){return info}
export function setGetInfo(arg){info = arg}

每当我们看到可能被各处代码污染的数据,这总是我们应对的第一招。

至少你可以看见修改它的地方,并开始控制对它的访问,随后最好将这个函数转移到一个类或者模块中,只允许模块内部的代码使用它,从而尽量控制其作用域

全局数据印证了帕拉塞尔斯的格言:良药和毒药之间的区别就在于剂量。有少量的全局数据或许无妨,但数量越多,处理的难度就会指数上升。

即使只有少量数据,我们也愿意将他们封装起来,这是软件演进过程中应对变化的关键所在