zoukankan      html  css  js  c++  java
  • 设计模式之再读重构

          这篇博客本来是帮朋友的教学网站写的系列课程,但是因为格式、案例等原因要让我重新修改,我这个人最烦的就是这些条条框框。所以一气之下就没有发出去,索性就直接写在自己的博客里,总感觉还是这样来的舒服、随意。

          重构(名词上的定义):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。重构(动词上的定义):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。在项目开发过程中,两顶帽子的故事:如果我们把项目开发过程中的添加新功能、以及重构想象成两顶帽子的话。那么在添加新功能时,你不应该修改既有代码,只管添加新功能。通过测试,你可以衡量自己的工作进度。重构时你就不能再添加功能,只管改进程序结构,此时你不应该添加任何测试,只在绝对必要时才修改测试。但是这两顶帽子,我们在实际的项目开发过程中,往往需要来回的替换,具体可看自己的项目进度衡量。

          在软件设计里面有个著名的破窗理论,大意为:在一座新房子里面,一旦开始出现一扇破窗,那么这扇破窗的下面将很快出现垃圾,同时房子的其他部分都将陆续出现破败的情况。将这个破窗理论借用到我们软件开发中,我们将会看到一个原来设计很好的架构系统,如果不注意维护的话,那么将很快出现第一个代码上的破窗。这个时候,如果我们不注意及时重构修补的话,那么破窗效应将蔓延,最终整个项目有可能面临失控的危险。所以重构带来的第一个好处,将是改进软件设计。

          常常会有人有疑问,在项目里面是不是应该拨出专门的时间用于代码重构呢?答案是否定的,重构应该是随时随地进行,你不应该为重构而重构,因为你想做别的什么事时,重构可以帮助你把那些事情做好。Don Roberts给了我们一条准则:第一次做某件事时只管去做;第二次做类似的事会产生反感,但是无论如何还是可以去做;第三次再做类似的事,你就应该重构。

          在极限编程里有这么一种观点:重构可以取代预先设计,就是说你根本不必做任何设计,只管按照最初想法开始编码,让代码有效运作,然后再将它重构成型。其实,我个人还是不同意这个观点的。因为一个系统的开始之初,肯定会伴随着一个设计思路的诞生。设计思路的诞生,必然会让我们联系自己的一些过往经验,评估出一种高可行、高稳定的方案出来。但是在这个过程中,不管多牛的人,都不可能设计出100%完美的系统。所以这个时候重构就将作为一种重要的手段,牵着系统走向完美、稳定之路。下面的内容将具体讲解一下,重构的典型案例,以及如果重构。

          相比大家都听说过代码的坏味道吧,Martin Fowler在一次去苏黎世拜访Kent Beck交流重构这个微妙问题时候,因为受到刚出生的女儿的气味影响,提出来用味道形容代码。他们将一些写的不好的代码,称作该代码有坏味道。但是我们不需要提出什么精确的标准来衡量代码坏味道,因为当我们有一定代码量之后,我们自己就将发现重构有的时候凭的就是一种直觉。当我们看到一段很长的代码之后,就会想要将其变小、变简单。当我们看到一个方法的名字里面包含1、2、3这样的随意命令时,就会想要将其改掉。这就是一种重构的嗅觉,当我们有了很灵敏的嗅觉之后,就能够很容易的闻出代码的坏味道。

          在代码的众多坏味道里面,首当其冲的就应该是重复代码。为什么说重复代码是最坏的味道呢?下面笔者就讲一个自己的亲身经历:一个涉及到金额支付的App项目里面,包括两个页面“首页面”和“支付页面”。在首页面的时候,它会展示所有的支付方式里面金额最低的那一个(因为针对不同的支付方式,会有不同的优惠活动);然而点击到支付页面的时候,它将详细的展示所有的支付方式以及价格。虽然,以上两个页面展示的形式不同,但是金额的计算方法却是一样的。早先也许是忙于赶进度吧,两个页面各自计算,这样就导致了一次严重的上线事故。因为在一次计算公式变更的时候,开发人员只修改了其一,而未改其二,导致了两处的价格不一致。

          然后第二种经常发生的坏味道就是过长的函数,拥有短函数的对象会活得比较好、比较长。“间接层”所能带来的全部利益---解释能力、共享能力、选择能力---都是由小型函数支持的。这个时候,有些人可能就会担心拆分过多的小函数会不会带来一定程度的性能损失呢?也就是说小函数越多,调用次数就越多,程序就运行的越慢。其实完全不需要担心这个问题,现代OO语言几乎已经完全免除了进程内的函数调用开销。最终的效果是:你应该更积极地分解函数。我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。我们可以对一组甚至短短一行代码做这件事。哪怕替换之后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫地那么做。如果函数内有大量的参数和临时变量,它们会对你的函数提炼形成阻碍。这个时候我们就可以考虑提取整个对象。如何确定该提炼哪一段代码呢?一个很好的技巧是:寻找注释。它们通常能指出代码用途和实现手法之间的语义距离。条件表达式和循环常常也是提炼的信号。

          一直以来代码界就存在一个比较有意思的话题,到底代码里面应不应该加注释呢?从嗅觉上说,注释不是一种坏味道,事实上它们还是一种香味呢。但是过犹不及,有些人过度使用,把注释当做一种除臭剂来使用。常常会有这样的情况:你看到一段代码有着长长的注释,然后发现,这段注释之所以存在是因为这段代码写的太糟糕。其实本质上来说,注释可以帮助我们更好的理解代码,找到代码里面坏味道。但是有的时候,如果我们对某段代码进行了修改,这个时候原来的注释可能就会变得多余或者已经具有迷惑性的不准确了。所以基于上面的分析,笔者自己持有的观点就是尽量少些注释,因为如果一段代码你需要添加注释去说明问题的话,那么就已经说明这段代码自解性太弱了。这个时候,我们就应该想办法重新调整变量或者函数的命名。

          其实在那篇系列教程里面,还讲解了很多优化重构的方法、技巧。在这里笔者就不想多说了,但是有一点多态替换switch、if等条件判断语句,在我们平时的项目里面肯定会经常用到,具体的做法就是将每一条判断条件拆分为一个对象,当需要新的判断条件时只需要新建对象即可。这样就是面向扩展开放、面向修改关闭了。

          如果有什么写的不对的地方,欢迎拍砖,see you!

  • 相关阅读:
    002梯度下降
    001-线性回归
    可视化库SEABORN基本操作2/2
    可视化库Seaborn基本操作1/2
    数据可视化库-Matplotlib基本操作
    数据分析处理库-pandas
    向量点乘(内积),叉乘(外积)
    科学计算库Numpy基础操作
    django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call
    ORM 模型
  • 原文地址:https://www.cnblogs.com/xiaocai20091687/p/xiaocai_design_eight.html
Copyright © 2011-2022 走看看