文章目录

  • 第二次阅读
    • 再版序
    • 译序
    • 前言
    • 第2章
      • 2.1 何谓重构
      • 2.2 为何重构
      • 2.3 何时重构
      • 2.5 重构的难题
      • 2.6 重构与设计
    • 3. 代码的坏味道
      • 关于编程语言的说明
      • 3.1 重复代码
      • 3.2 过长函数 long method
      • 3.3 过大的类 large class
      • 3.4 过长参数列表 long parameter list
      • 3.5 发散式变化 divergent change
      • 3.6 霰弹式修改 shotgun surgery
      • 3.7 依恋情结 feature envy
      • 3.8 数据泥团 data clumps
      • 3.9 基本类型偏执 primitive obsession
      • 3.10 switch表达式 switch statements
      • 3.11 平行继承体系 parallel inheritance hierarchies
      • 3.12 冗赘类 lazy class
      • 3.13 夸夸其谈未来性(高估未来的可能性) speculative generality
      • 3.14 临时字段 temporary field
      • 3.15 过度耦合的消息链 message chains
      • 3.16 中间人 middle man
      • 3.17 狎昵关系(不合适的亲昵关系) inappropriate intimacy
      • 3.18 异曲同工的类 alternative classes with different interface
      • 3.19 不完美的库类 incomplete library class
      • 3.20 纯数据类 data class
      • 3.21 被拒绝的馈赠 refused bequest
      • 3.22 过多的注释 comments

第二次阅读

这本是作者第二次阅读,首次阅读是任务驱动的,当时正在进行一项大型的重构,吸取袁英杰顾问的设计思路,对原来质朴的设计思想进行了归纳,对业务进行了更深度的建模,对代码写作模式进行了重定义。那时候为了追上顾问的思路,阅读了《重构》和设计模式相关资料,对个人提升作用很大。

好书得读多遍,大半年后的今天,开始第二次阅读。这次阅读不带任务,纯粹是想从中再汲取些知识,也许会有更深刻的体验。

再版序

再版序中提到两种常见的观点。

  1. 只要掌握重构思想就足够了,没必要记住详细的重构手法。
  2. 有很多人打打着重构的大旗,大刀阔斧进行修改,干脆重做整个系统。

对于第一点,我是部分赞同的,最主要的原因是没那么多的精力去细抠,只能边学边用。第二点我点头同意,不少重构实际上是进行“重写”。

书的作者对重构最基本的定义是:“不改变软件可观察行为的前提下改善其内部结构”。有时候需要通过添加测试,来作为重构的立足点。

还有一个有趣的观点是,相要重构真正发挥威力,就要从“让人困惑的软件行为”入手。为什么?因为能看懂的代码,就说明还没那么迫切的需要进行重构!interesing!从这一角度入手,我们不需要刻意挖掘代码中的坏味道,当你感觉看两眼还没搞懂,那就是一个需要重构的点。

没搞懂:通常意味着坏味道。坏味道,是重构的入手点。找到坏味道,就需要做法指导
加粗的两点,个人认为是本书的核心内容。

个人感悟 :现代的IDE有很强大的功能,“一键重构”将一些模式化的重构变得非常容易且不易出错,不过我们不能滥用这个功能,不要忘记重构的初心,是解决坏味道,而不是引入更多坏味道。

译序

设计模式不仅是一些具体问题的解决方案,更是一种对追求模型的追求。

但话又说回来,实用压倒一切,产品经理最希望看到的是能用、不出问题、快速交付,两个角色的利益实际上是有一定的冲突的。平时工作中听到很多人说到“因为领导催的紧,所以没好好写”,然后遗留一堆技术债务和恶心的代码,坑了后面的人。

个人认为一个懂软件的PO才能真正的把开发速度和产品质量做到一个平衡,一个合格的程序员也应该把持续重构的意识培养起来,速度和质量都要兼顾。

“重构、设计模式”是帮助自己写出高质量代码的方法论,懂这些工具,面对再复杂的业务,也能从容不迫,保证质量同时维持开发效率。

序章节中的核心观点提炼:

  1. 框架不可能一开始就完全正确,是一个进化的过程。因此代码被阅读和修改远远多于被编写的次数。因此需要重构来保持代码易读易修改
  2. 重构的风险:容易引入错误,如果不做好准备,不遵守规则,风险越大。容易越陷越深,给自己挖一个大坑。因此重构必须系统化的进行。
  3. 重构的目标是达到某种设计模式,但设定目标远远不够,如何达到目标是另一个难题。

前言

前言里提到了案例,是产品进度和重构之间的矛盾,主要想引出一个观点就是“以重构方式改进软件质量”。

  1. 坚持以持续不断的重构行为来整理代码。
  2. 重构的定义:不改变外在行为,改进内部结构。是一种程序整理方法,最大程度减小整理过程中引入错误的概率。
  3. 在代码写好之后改进它的设计。设计不再是一切的前提!

书籍阅读指导:

  • 了解重构的原则、定义、进行重构的原因。 第一、二章
  • 代码坏味道,第三章
  • Java测试框架,第四章
  • 重构手法列表,第五~十二章
  • 重构应用于商业化开发的问题,第十三章
  • 重构自动化工具,第十四章
  • 总结,十五章

第2章

2.1 何谓重构

对软件的一种调整,不改变外部行为,提高理解性,降低修改成本。
添加新功能和重构不应同时进行。——这一点我也一直再坚持,用好版本控制和分支。

2.2 为何重构

  1. 重构改进软件设计。整理代码,避免腐化,消除重复代码,所有事物和行为在代码中只表述一次。
  2. 重构使软件更容易理解。准确的写出我所要的。还可以利用重构帮助自己理解不熟悉的代码,重构并通过测试用例则可代表自己理解了代码。——很受用的思想,为了理解代码而去重构。把代码整理出层次感,可加速自己理解代码框架。
  3. 重构帮助找到bug。这是2的副产物,理解代码后,把新的理解反馈回去。
  4. 重构提高编程速度。良好的设计是快速开发的根本。

2.3 何时重构

  1. 添加功能时。——重构完了更好理解业务,知道怎么更方便的加功能。
  2. 修补错误时。有bug是该重构的一个信号。
  3. 复审代码时。

2.5 重构的难题

数据库 、 修改接口、难以通过重构手法完成的设计改动、何时不该重构而应该重写。
几个摘抄:

  1. 发布接口很有用,但也有代价。除非真有必要,不要发布接口

让我想到“平台”和“业务“对待接口的不同的态度,平台发布接口需要业务做较多的适配,而业务更灵活些,因为没有依赖于业务的更上层的东西了。

  1. 对象模型和数据库模型之间的分割层,可以隔离两个模型各自的变化

这正是我之前参与过的一个重构项,早期开发内存对象数据结构和数据库是一样的。随着对性能要求的提升,数据库存储进行了一系列的改造。改造前,团队评估了改造的成本,发现加入这么一层隔离层能够有效的降低后续的修改量,稍专业点的话来说就是“隔离变化”。

  1. 在新旧接口并存的场景下,尽量让旧接口调用新接口。

当接口的使用者全部迁移到新接口时,接口提供者将会很方便的删掉没用的代码。

  1. 重构之前,代码必须起码在大部分情况下正常工作。这是一个判断该重构还是重写的标准。
  2. 越接近项目的最后期限,就越应该避免重构。有个很好的形容,未完成的重构是“债务”,很多公司需要借债来使自己更有效的运作,但借债就得偿还利息,过于复杂的代码所造成的维护和扩展的额外成本就是利息。

含义就是保持适当的负债率,根据项目进度决定债务是否偿还。

2.6 重构与设计

观点摘抄:

  1. 有了设计,我可以思考的更快,但其冲充满了小瑕疵。
  2. 重构可以取代预先设计?比较极端的想法,但确实可行。不过适当的设计仍然是很好的实践方案。
  3. 不同的想法——> 得到一个解决方案——>开始编码——>重构。在实现最初的想法的时候,对业务的理解会变深,可能察觉到最优的方案和当初的设想不同,只有重构利器在手,就不成问题。
  4. 重构带来更简单的设计,同时又不损失灵活性,这也降低了设计过程的难度,减轻了设计的压力。

3. 代码的坏味道

这一章个人认为是本书的精华。它解释了“该在什么时候做某个动作”。
我之前对一段代码进行重构,往往凭的是感觉(在重构这本书里,同样的说法叫“编程美学”),没有进行过系统的分类和总结。
这本书将这些感觉叫做坏味道,非常形象。
仔细揣摩作者意图,这实际上是在给重构总结模式,模式往往不是凭空产生,而是大量的行为之后的一种分类和总结,让后面的行为有迹可循。更深一步思考,这不仅可用在重构这件事情中,生活中很多地方我们需要进行模式总结,形成自己的方法论,对遇到的新问题就可以有枪可使。

关于编程语言的说明

本书中用的是Java,我平时主要用C/C++和golang,他们在语法、理念上和java略不一样,不过大体上还是能找到对应的关系。

golang里淡化的类的概念,多态、继承在golang里对应着接口和匿名组合,阅读时对于golang要转化下思路。

3.1 重复代码

坏味道模式和解决这个坏味道的方法::

  1. 同一个类中两个函数有相同的表达式。使用Extract Method
  2. 两个互为兄弟的子类含有相同表达式。使用Extract Method + Pull Up, Form Template Method, Substitute Algorithm
  3. 毫不相关的类出现重复代码。使用Extract Class

注:

  • 由于Java所有函数都要有类承载,Extract Class在golang/c里可以用Extract Method替代,这样就不需要考虑函数归属于哪个类了。
  • Pull Up(推入超类)在golang里就是两个struct(A, B)匿名组合一个struct(作为超类S),S里实现公共的方法。

本能的厌恶重复代码,因为我经常参加一些重构活动,不止一次的为一些很细微差异的重复代码而感到困惑。不知道是业务本身就是有差异,还是因为原来的作者写错了,不负责任的copy代码,最后都是要其他人来擦屁股,导致团队整体效率下降。

有句话总结的好,同样一份逻辑仅应该有一份代码来实现它。

3.2 过长函数 long method

间接层、小函数,会带来解释能力、共享能力和选择能力。
多用小函数的坏处是函数调用性能降低、代码阅读不便。

让小函数容易理解的关键是一个好名字,以其用途来命名,而不是实现手法
每当感觉需要以注释来说明点什么时,就需要把说明的东西写进一个独立的函数中。

如果一个函数内有大量的参数临时变量,会对函数Extract Method造成阻碍。需要用一些消除临时变量的重构手段。例如,Replace Temp With Query。

对于过长的参数列表,使用Introduce Parameter Object(参数都放在一个数据结构里)和Preserve Whole Object(传某个对象的值变为传整个对象)。

大杀器:Replace Method with method object,引入一个函数对象来替代一个函数

条件表达式和循环也是需要提炼函数的信号,可以使用Decompose Conditional,对于条件表达式,用一个函数来表达一个与非或的逻辑,并给他一个符合语义的函数名。

3.3 过大的类 large class

个人经验:如果不加控制,代码中很容易形成上帝类,因为上帝类写起来最省心,什么数据都有,确实也是一种优势,不用考虑数据的归属,不用把函数字段从类之间搬来搬去。

那么过大的类有坏味道的说法是怎么来的呢?本书中,作者提到这容易导致重复代码,就是3.1提到的坏味道。

解决这个坏味道的方法:核心时分解

  • 用Extract Class 将几个变量提炼到新的类中,应选择彼此相关的变量。比如有相同前缀或词尾的变量。也可以Extract SubClass提炼出子类。
  • Extract Class有个技巧,先确定client如何使用,然后用Extract Interface为每一种方式提炼一个接口。这可以帮助你看清楚如何分解这个类。

接口思想在golang里再常见不过,因为没有继承。

  • 如果上帝类是个GUI类,可能需要把数据和行为移到一个独立的领域对象去,需要在两边保留一些重复的数据,并保持两边同步。即Duplicate observe data(Observe 模式的经典应用、MVC框架、前后端行为分离,数据同步)。

3.4 过长参数列表 long parameter list

当数据归属拎得清时,参数列表通常都会比较短,类的this,(golang的Receiver)中就应该包含这个函数所需打绝大部分数据。

太多参数会造成前后不一致、不易使用。因此过长的参数是一种坏味道。

解决这个坏味道的方法:

  • Replace Parameter with method(如果函数能够通过其他途径获取参数值,就不应该传参,比如类内的函数直接调用)
  • Preserve whole object, Introduce Parameter Object,之前《3.2 过长函数》中提到过。

3.5 发散式变化 divergent change

第一次阅读时,感觉3.5和3.6有点类似,其实说的是一个话题的两种坏味道。第二次阅读时想到了一个比喻:我们把不同的需求定义成不同的靶子,把不同的定义成不同的

最好的模式就是,一把枪专门射一个靶子。使外界变化与需要修改的类趋于一一对应。

发散式变化的坏味道指的是一个枪打多个靶子(某个类经常因为不同的原因在不同的方向上发生变化),霰弹式修改指的是多个枪打一个靶子(每遇到某种变化,都必须在许多不同的类内做出许多小的修改)。

在正交设计四原则里,这个又被描述为多个变化导致一处修改(多个变化方向),解决的办法时分离不同的变化方向

解决这个坏味道的方法:

  • Extract Class,遵循一个原则:针对某一外界变化的所有相应修改,都只应该发生在单一类中,而这个新类内的所有内容都应为此发生变化。为此,应该找出某特定原因而造成的所有变化,然后使用Extract Class将他们提炼到一个新类中。

3.6 霰弹式修改 shotgun surgery

与3.5相对应的坏味道,每遇到一个变化,都需要在不同的类中做出许多小的修改。

在正交设计四原则里,这个又被描述为一个变化导致多处修改(重复),解决的办法时消除重复

解决这个坏味道的方法:

  • Move Method,Move Field把要修改的代码放入同一个类。
  • 用Inline Class把一系列相关行为放入同一个类,可能造成3.5的坏味道,但可以轻易的消除。

3.7 依恋情结 feature envy

设计一个类的一个原则是:将相关的数据和行为包装在一起。
这个坏味道是指一个类过多依赖另一个类的数据,违反了上面的这个原则。

注:Strategy模式和Visitor模式为了解决发散式变化,会造成一定的feature envy。

解决这个坏味道的方法:

  • Move Method 整体搬移函数。
  • ExtractMethod+MoveMethod,先提取,再搬迁。

3.8 数据泥团 data clumps

类出现的一个目的就是将相关数据包在一起。
相比较散落的数据,类提供了一个树状的层次关系,可以一揽子的把相关数据打包在一起。把散落的数据看作一个一个节点,那么就是它们的根。
又如同UML图里的组合关系。

在代码中,数据泥团出现在:两个类中相同的字段、函数入参中相同的参数。

解决这个坏味道的方法:

  • Extract class,抽取到一个类中。
  • Introduce Parameter Object,Preserve whole object给函数签名减肥。

数据移走之后,行为也要移走!否则会产生3.7中的feature envy。

3.9 基本类型偏执 primitive obsession

核心就是把基本类型赋予类的属性。

对于golang来说,就是简单的

type MyInt int // 用type重新定义int
func (i MyInt) Method1(){}

给基本类型引入方法。又比如,数值+币种的money类,起始值+结束值的range类,电话号码、邮政编码类……

解决这个坏味道的方法:

  • Replace Data Value With Object将原本单独存在的数据值替换为对象。对象可以做的事情就比较多了。
  • 如果替换的类型码,type1,type2之类,可以用Replace Type Code with Class
  • 如果是类型码的表达式,if xxx==type1,可以用Replace Type Code with subClass,或者Replace Type Code with State/Strategy。
  • 对于数组,可以用Replace Array with Object,为array[0], array[1], array[2]赋予新的名字,get方法。

3.10 switch表达式 switch statements

3.11 平行继承体系 parallel inheritance hierarchies

3.12 冗赘类 lazy class

3.13 夸夸其谈未来性(高估未来的可能性) speculative generality

3.14 临时字段 temporary field

3.15 过度耦合的消息链 message chains

3.16 中间人 middle man

3.17 狎昵关系(不合适的亲昵关系) inappropriate intimacy

3.18 异曲同工的类 alternative classes with different interface

3.19 不完美的库类 incomplete library class

3.20 纯数据类 data class

3.21 被拒绝的馈赠 refused bequest

3.22 过多的注释 comments

— to be done

《重构》——第二次阅读笔记。golang视角相关推荐

  1. 西瓜书第二章阅读笔记

    西瓜书第二章阅读笔记 第二章 模型评估与选择 1.经验误差与过拟合 2.模型评估方法 2.1 留出法 hold out 2.2 交叉验证法 cross valildation 2.3 自助法 boot ...

  2. Mastering ROS for Robotics Programming第二版阅读笔记

    Mastering ROS for Robotics Programming一书由Lentin Joseph和Jonathan Cacace编写,目录如下: 书中所用代码可以在github上找到,例如 ...

  3. 大道至简第二篇阅读笔记

    流于形式的沟通 1.C语言是程序员与计算机交流的语言,而不是他与客户交流的语言.程序员面对的是计算机,但计算机不是客户. 2.与客户如何交流?他们的解决之道是模型语言. 3.现场客户:这是极限编程的特 ...

  4. 极限编程阅读笔记--第二篇

    应本学期软件工程寒假作业,发表第二篇阅读笔记,读的书为<规划极限编程>,本次阅读了五到八章. 第五章:本章讲的是概述,在开发之前我们要总揽这个项目,看看他的开发周期和业务周期,一般开发周期 ...

  5. 网约车需求预测文献阅读笔记(一)《基于图卷积的出发地—目的地矩阵预测:旅客需求建模的新视角》

    文献阅读笔记:<基于图卷积的出发地-目的地矩阵预测:旅客需求建模的新视角> 摘要 现有研究不足 研究挑战(难点) 本文的贡献 准备工作 定义1:格子 定义2:时间片 定义3:OD矩阵 模型 ...

  6. INSPIRED技术产品经理的天启(第二版)-【英文书】阅读笔记NO.2

    INSPIRED技术产品经理的天启(第二版)-[英文书]阅读笔记NO.1 INSPIRED技术产品经理的天启(第二版)-[英文书]阅读笔记NO.3 第一章 每一个伟大产品的背后 作者的信仰和本书的中心 ...

  7. INSPIRED技术产品经理的天启(第二版)-【英文书】阅读笔记NO.1

    本书全名为<INSPIRED: HOW TO CREATE TECH PRODUCTS CUSTOMERS LOVE>(Wiley) 中文直译过来就是<启发:如何创造客户热爱的科技型 ...

  8. 《无线网络技术教程第二版》阅读笔记(一)

    <无线网络技术教程>阅读笔记(一) 无线局域网的定义: WLAN是计算机网络与无线技术相结合的产物,通常指采用无线传输介质的计算机局域网, 其利用无线电和红外线等无线方式,提供对等或点对点 ...

  9. 计算机网络阅读笔记——第二章物理层

    文章目录 思维导图 阅读笔记 1.物理层的基本概念 2.数据通信的基础知识 3.物理层下面的传输媒体 4.信道复用技术 5.数字传输系统 6.宽带接入技术 习题解答 1.常见问题与解答 2.习题与解答 ...

  10. 【阅读笔记】精益开发实践用看板管理大型项目

    [阅读笔记]精益开发实践用看板管理大型项目 参考 精益开发实践用看板管理大型项目 文章目录 [阅读笔记]精益开发实践用看板管理大型项目 一.我们如何工作(案例研究) 1.项目背景 2.组织团队 3.每 ...

最新文章

  1. 皮克斯技术指导辞职读博:研究AI设计怪物,从《游戏王》卡牌开始
  2. 打开和关闭mysql服务器_启动和关闭MySQL服务器
  3. 【ABAP】获取后台Job相关状态
  4. 二叉搜索树(创建,插入,删除):基础篇,适合新手观看。
  5. Jquery,ajaxFileUpload插件完成图片上传
  6. 脱离 Windows 完全使用 Linux你花了多少时间适应?
  7. 使用AT指令给飞信号发短信失败
  8. Unity编辑器拓展--Hierarchy拓展
  9. HyperX旋火无线游戏鼠标,摆脱“线”制,黑白双煞争分夺秒
  10. 技法(但请少用):为非活动窗口绘制活动的标题栏
  11. 分享程序员面试的7个技巧
  12. 移动管家汽车手机蓝牙无钥匙解锁方案
  13. 你最该学的职场必修课[职场规划个人笔记]
  14. Windows的任务管理器怎么显示进程的图标
  15. echarts 热点泡泡图
  16. 树(二)——二叉树遍历
  17. 游戏外包开发技术难点分析
  18. 快速实现软件试用的解决方案
  19. 风信子网络工作室介绍
  20. leaflet动态绘制圆、多边形

热门文章

  1. No instances available for provider
  2. 使用阿里云云服务器遇到大流量攻击怎么防御
  3. 知识分享!什么是中继器?-道合顺大数据Infinigo
  4. 2021-04-30
  5. ubuntu下查看硬件配置
  6. 【02】制作第一个zblog模板第二期,熟悉模板所有的开发运行和调试机制·提前做好所有纯html页面
  7. mkv转换mp4格式,mkv格式转mp4
  8. 结对开发石家庄地铁线路
  9. 一个女生最好的生活状态
  10. 2022年最新手机处理器性能排行榜,手机处理器天梯图