【书中自有黄金屋】《重构-改善既有代码的设计》读书笔记
前言
为何阅读这本书
阅读这本书的初心在于,半年多的时间里一直在重构项目代码。
阅读了不少人移交过来的代码,项目代码整体看下来,就会发现代码的阅读起来非常费劲,并且复用性和拓展性都很差,另外代码逻辑上太绕让人费解。看这样的代码,简直内心在奔腾。
重构时,就会一边骂着一边改着,这种改造简直就是挑战人性,给人非常不好的体验。
在重构完一个项目时,就会发现糟糕代码的很多通病。
就是完全没有任何规则和规范,按照自己的规则来写,而好的代码会有一套规则约束,有很明确的规范。正因为脑中没有概念和理论支撑,才会写的如此【随心所欲】。
让我想起了托尔斯泰的一句话:幸福的人是相似的,不幸的人各有各的不幸。换做写代码就是:优雅的代码都是相似的,糟糕的代码各有各的毛病。
而理论支撑和方法论在哪呢?就要通过学习和阅读来获取。所以就找来这本书来阅读,以深刻深入学习这套理论。
花费5小时时间阅读完成本书,又花费3天下班时间将书中的观点进行汇总。整体而言,这本书的绝大部分观点在这次重构项目中都得到了应用和实践,所以在看到这些观点和方法时,会理解的更加深刻。
心得体会
开发
提炼短函数,保持单一职责原则,函数和数据应该被包装在形式良好的单元内。并将函数以用途命名
函数应保持变化只在一处发生,如果不能保持则应考虑分离出变化的部分
几个类中相同部分,应考虑提炼/分解函数,并继承。而代码太分散,应考虑合并代码
降低类、方法和值域的耦合性,可考虑移动、提炼相关内容
减少临时变量;临时表达式可考虑final。复杂表达式应考虑创建临时变量
调用函数中参数过多,应考虑提炼类或者用类来表示
多余的代码和注释就应被去掉
测试
小步前进、频繁测试
临界值测试
不断丰富测试用例
重构记录
- 应记录下来重构的过程、动机、案例、图
思维导图
书中汇总
代码的坏味道
重复代码
Q:冗余代码
R:提取相同的代码到一个方法/类中,分割开来相同和差异部分
过长函数
Q:过长难以理解
R:提取分解成不同的短函数,并用其用途进程命名
R:条件式和循环也可以提取函数
R:遵循单一职责原则,不同作用的代码抽取到不同的类中
过大类
Q:类中过多的代码,可能会造成代码重复、混乱
R:提炼类和子类,为每一种使用方法提炼不同的接口
过长参数列
Q:参数太多不易理解,可能会前后不一致,后续调用函数需要很多参数
R:只传需要的参数
R:用封装的实体类
发散式变化
Q:某个类受多种多种变化的影响
R:改变只应发生在单一类中,以应对不同外界的变化
散弹式修改
Q:每遇到变化,就需要多处不同的类做出小修改以响应之
R:移动类和方法,把所需的所有需要修改的代码放进同一个类中
依恋情结
Q:函数对某类的兴趣高于对自己所处的host class的兴趣
举例:某函数为了计算某值,从另一个函数对象调用几乎所有半打的取值函数
R:把函数移动到该去的地方。移动原则:哪个类拥有最多被此函数使用的数据,就把这个函数和那些数据放一块
数据泥团
Q:两个类内相同值域、许多函数签名式中的相同参数,就可以抽取自己的类
R;相同的属性抽取到一个类中
基本型别偏执
Q:使用大对象会增加性能开销
R:编写小对象,比如money类、表示范围的range
R:将原本单独存在的数据值替换为对象
switch惊悚现身
Q:switch表达式带来重复
R:运用多态
平行继承体系
Q:每为某一个类增加子类,另一个类也须增加一个子类。会发现,继承体系的类名前缀和另一个的完全相同,则需要分离两个体系
R:让一个继承体系的实体指涉(参考、引用、refer to)另一个继承体系
冗赘类
Q:类没有价值/多余
R:直接干掉多余的类
夸夸奇谈未来性
Q:以为在某天就会用到,并做一些非必要的处理
R:去除掉不必要的参数、测试类等
令人迷惑的暂时值域
Q:某个实例变量仅为某种特定情势而设,但并不是所有都会用到
R:利用提炼函数将这些变量和其相关的函数提炼到一个独立的类中
过度耦合的消息链
Q:一个对象索求另一个对象,形成一个消息链。耦合性太高
R:看最终得到的对象是什么用途,然后看是否可以提炼函数
中间转手人
Q:过度抽取函数/委托函数
R:直接和实责对象打交道,而去除掉不必要的中转
狎昵关系
Q:两个类太亲密,需要花费很多的时间去探究类关系
R:移动方法或者类,以划清界线
R:抽取共同点到一个类中
异曲同工的类
Q:两个函数做同一件事,却有不同的签名式
R:根据用途重命名类,或者直接抽取共用的部分
不完美的程序库类
Q:程序库类不能满足需要
R:引用外部方法,或引用本地拓展
幼稚的数据类
- 没有用处的类
被拒绝的遗赠
Q:子类中不需要的函数,就不该继承
R:抽取需要的父类,然后继承另一个类
过多的注释
Q:注释过多会引起误会
R:去除掉不必要的注释
构建测试体系
自我测试
频繁进行测试
每个类都应该有一个用于测试的main(),可能不好实现
JUnit测试框架
测试套件
- 测试代码
测试用例
独立的测试类
测试开始,先让它失败,比如错误期望值、导致失败/异常的值
单元测试与功能测试
测试装备
添加更多测试
观察类所做的一切,然后针对任何一项功能的任何一种可能的失败情况,进行测试
测试应该是风险驱动
寻找边界条件:寻找特殊的、可能导致失败的情况
不断丰富测试案例
一个测试类包含另一个测试类
测试应集中在容易出错的地方
重构名录
重构的记录格式
名称
- 建造一个重构词汇表
简短概要
重构手法的适用场景,它所做的事情
一个简短文句,介绍这个重构能够帮助的问题
一段简短陈述,介绍该做的事情
一幅速写图,简单展现重构前后示例;可展现代码,也可展现UML图
动机
为何需要这个重构
什么情况下不该重构
作法
简明扼要地介绍如何一步步的进行此重构
可以在未来回忆,快速记得怎么做的
每个步骤都写得简短
要安全的重构方式,应采用非常小的步骤,并在每个步骤后进行测试
范例
以一个十分简单的例子说明此重构如何运作
帮助解释重构的基本要素
寻找引用点
这些重构准则有多成熟
小步前进、频繁测试
引入设计模式
重新组织你的函数
提炼函数
动机:过长函数,或一段需要注释才能理解的代码
函数长度不是问题,关键在于函数名称和函数本体之间的语义距离
作法
创建新函数,名以它做什么来命名
颗粒度要小,单一原则,每个函数做一件事
将函数内联化
- 可以直接使用函数,不需要间接调用。
将临时变量内联化
将变量引用的动作,替换为它赋值的那个表达式本身
如果临时变量并未被声明为final,那就声明为final,然后编译
以查询代替临时变量
- 应减少临时变量,将临时变量的表达式放在独立函数中
引入解释性变量
- 复杂的表达式,可以用临时变量将表达式分级,用变量名称解释表达式用途
剖解临时变量
临时变量应被赋值一次,如果临时变量承担太多责任,则应被拆解变量。
final作用在临时变量,只承担一个责任
移除对参数的赋值动作
对对象进行赋值,则相当于改变了引用关系。会降低代码的清晰度,且混淆传值和传址的方式
建立一个临时变量,把待处理的参数值赋予它
可在参数上加关键字final,从而强制它遵循不对参数赋值。不过建议在长函数用final
如果返回的值有很多,可以封装成一个对象
以函数对象取代函数
局部变量太多不易提炼函数,可以用函数对象来替换
将常用的变量、值域抽取为函数对象
替换你的算法
将算法替换为更清晰的算法
测试:调用重构前后的方法,看返回值是否一致
在对象之间搬移特性
搬移函数
- 某个类使用的次数比较多,则可以抽取函数到一个类中
搬移值域
- 某个值域被其所驻类之外的另一个类更多的使用,则可以调整位置
提炼类
一个类应该是一个清楚的抽象,处理明确的责任
考虑哪些可以分离出去,并分离成一个单独的类
对于函数中的某些数据经常同时变化甚至相依,应将其分离出去
将类内联化
- 类没有承担足够的责任,则应该合并函数
隐藏委托关系
- 某个类建立客户所需的所有函数,用以隐藏委托关系
移除中间人
引入外加函数
- 建立一个函数做调用
引入本地拓展
- 添加额外的函数以拓展函数
重新组织数据
自封装值域
- 为待封装值域建立取值/设置函数
以对象取代数据值
- 为待封装值域建立一个类
将实值对象改为引用对象
- 可使用多个对象作为新对象的访问点
以对象取代数组
- 数组不同元素的位置难以记住且难以理解
复制被监视数据
- 实现良好分层
以符号常量/字面常量取代魔法数
- 魔法值无法正常说明数字含义
封装值域
public调整为private
为public值域提供取值、设值函数
以数据类取代记录
以策略/状态取代型别码
- 运用设计模式,每个类是不同的类别
简化条件表达式
分解表达式
如if中有非常复杂的表达式,应提炼成一个独立的函数
形成新分支和新函数,可以突出条件逻辑,更清晰表明每个分支的作用
合并表达式
合并重组的条件片段
鉴别出执行方式不随条件变化而变化的代码
共通的代码不止一条,就应考虑提炼函数
移动控制标记
- 不用通过标记来判断是否结束循环,可以直接用break或return
以卫语句取代嵌套条件式
某个条件极其罕见,就该单独检查该条件,并在条件为真时立刻从函数中返回
卫语句要么从函数中返回,要么抛错
以多态取代条件式
- 抽取抽象函数
引入null对象
引入断言
简化函数调用
重新命名函数
- 描述清楚函数的用途
添加参数
移除参数
将查询函数和修改函数分离
- 将查询和修改的函数分离
令函数携带参数
- 若干函数的工作是相似的,但函数却包含了不同的值,可统一函数
以明确函数取代参数
- 函数内完全取决于参数值而采用不同的反应,则应为每个可能值建立一个独立函数
保持对象完整
- 若将对象中的某几个数据作为参数传递给函数,则可改为使用传递整个对象
以函数取代参数
- 获取结果可以用函数
引入参数对象
- 某些参数同时出现,可能分布在不同类中,那么可以抽取到一个类中
移除设置对象
隐藏某个函数
- 某个函数没被其他类用到,就改为private
以工厂函数取代构造函数
封装向下转型动作
- 某个函数返回的对象,需要由函数调用者执行向下转型动作,将向下转型动作移到函数
用异常取代错误码
以测试取代异常
处理概括关系
值域上移
- 两个值域相同的应移到父类
函数上移
构造函数本体上移
- 子类中拥有一些函数,它们的本体代码几乎完全一致,那么应在父类中新建一个构造函数,并在子类构造函数中调用它
函数下移
值域下移
提炼子类
- 类中的某些特性只被某些实体用到,新建一个子类,将其特性移至子类
提炼父类
- 两个类有相似特性,那就为这两个类建立一个父类,将相同特性移至父类
提炼接口
- 提炼相同的接口子集
折叠继承关系
- 父类和子类没有太大区别,就合并为一体
塑造模板函数
- 继承是避免重复行为的一个强大工具
以委托取代继承
- 某个子类只使用父类接口的一部分,或根本 不需要继承数据,那么就为子类建立一个值域,或建立一个委托的类,然后去掉两者的继承关系
以继承取代委托
大型重构
梳理并分解继承体系
- 某个继承体系同时承担两项责任,建立两个继承体系,并通过委托关系让其中一个可以调用另一个
将过程化设计为对象设计
- 将数据记录变成对象,将行为分开,并将行为移入相关对象之中
将领域和表述、显示分离
提炼继承关系
【书中自有黄金屋】《重构-改善既有代码的设计》读书笔记相关推荐
- 重构-改善既有代码的设计 读书笔记
这本书真的很赞,有种醍醐灌顶的感觉,一如既往,我还是大致分析一下这本书的思想,而不是单纯的文字摘录. 首先,无论是类还是方法本身,都可以视作对象.重构则是研究怎么降低耦合度的一种技术,同时我还是很支持 ...
- 重构-改善既有代码的设计读书笔记
1.基本重构 1.1 提炼函数(Extract Function) 将意图和实现分开,如果需要花时间浏览一段代码才能弄清它到底在干什么,那么就应该将其提炼到一个函数中,并根据它所做的事为其命名. 1. ...
- 书中自有黄金屋?其实你这样读书真的没用!
--<励学篇> 富家不用买良田,书中自有千钟粟. 安居不用架高楼,书中自有黄金屋. 娶妻莫恨无良媒,书中自有颜如玉. 出门莫恨无人随,书中车马多如簇. 男儿欲遂平生志,五经勤向窗前读. 这 ...
- 书中自有黄金屋系列6:读《浪潮之巅》-下篇
最新最全的文章请关注我的微信公众号:数据拾光者. 摘要:上一篇书中自有黄金屋系列6:读<浪潮之巅>-上篇 主要讲苹果.微软和雅虎的兴衰史.本篇主要讲下"不作恶"的谷歌以 ...
- 书中自有黄金屋系列1:读《腾讯传》
微信公众号:数据拾光者.愿结交更多的小伙伴,一同走人生路. 摘要:这是书中自有黄金屋系列的第一篇:读<腾讯传>.吴晓波写的这本<腾讯传>很好的讲了腾讯从建立到现在成为互联网双雄 ...
- 重构:改善既有代码的设计(软件开发的不朽经典)
重构:改善既有代码的设计(软件开发的不朽经典) 基本信息 作者: (美)Martin Fowler 译者: 熊节[同译者作品] 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:978 ...
- 重构—改善既有代码的设计
概述 1.1 参考资料 <重构-改善既有代码的设计>读后总结 <重构改善既有代码的设计> 22种代码的坏味道,一句话概括 1.2 何谓重构 首先要说明的是:视上下文不同,重构的 ...
- 『重构--改善既有代码的设计』读书笔记----序
作为C++的程序员,我从大学就开始不间断的看书,看到如今上班,也始终坚持每天多多少少阅读技术文章,书看的很多,但很难有一本书,能让我去反复的翻阅.但唯独『重构--改善既有代码的设计』这本书让我重复看了 ...
- 《重构-改善既有代码的设计》-第1例:租赁影片(2)
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 上接 重构-改善既有代码的设计-第1例:租赁影片(1) 2 运用多态取代与价格相关的条件逻辑 2 ...
- 《重构-改善既有代码的设计》-第1例:租赁影片(1)
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 买了<重构 - 改善既有代码的设计 >一书,一直没有好好看,大致过了下也觉得只是有点点印 ...
最新文章
- release和retain还有多少人在用
- 2021长郡浏阳中学高考成绩查询,2021长沙市高中排名一览表
- 高并发核心Selector详解
- WinForm 限制同一个进程只能打开一次
- Web公路,新手上路!NO.2 [ 基础标签(一)]
- Windows软件调试学习笔记(1)
- HDU1999不可摸数-暴力打表
- SCI论文写作训练营笔记汇总02_英文科技论文阅读与解析
- 输入输出数组元素的函数重载_C ++函数重载| 查找输出程序| 套装3
- pymavlink 源码剖析(一)之XML文件的数据解析
- 判定两个点是否在一条直线的同一侧_计算几何01-判定两条线段是否相交
- 随想录(risc cpu的那些事)
- python 安装xlwt失败_安装python库xlwt的时候遇到超时的情况。
- 上瘾:如何打造习惯养成中的产品(投资篇)
- MSN 与六度分隔理论
- PI AAE (Advanced Adapter Engine) 介绍一
- 商家又一必争之地!支付宝小程序一周年,带来哪些机遇?
- python 求 牛顿插值法中的差商表
- android:ellipsize
- Docker 第三章 容器