读书报告之《改动代码的艺术》 (I)
《改动代码的艺术》,英文名《Working Effectively with Legacy Code》,中文翻译的文笔上绝对谈不上“艺术”二字。愧对艺术二字(当然译者不是这个意思
)。书中第三部分不论是样例还是讲解都有点混乱,远不如《重构——改善既有代码设计》一书。
此书精华在于第一、二部分。
怎样学习这本书,作为一个最底层的码农,作为长期在别人代码上修修补补的苦逼二手货开发者,我仅仅能给的建议就是:你能够将它看做是怎样做定制功能的指导书——从某种意义上讲,非常多时候引入測试,实际就是加入一个叫做“測试”的定制功能。并且。这样似乎也恰好印证了该书的中文名”改动代码的艺术”。
其它的,我不想谈。也不懂。
就这样。
既然是要将这本书看做是怎样做定制功能的指导书,那么就先从本书第二部分“改动代码的技术”開始看。
1. 减少改动的风险
- 好的代码编辑工具。
哪些把二进制数据也能玩得出神入化的大牛。我就不考虑了。对像我这种普通猿类,没能吃上ALZ112。更别提ALZ113了。智商有限。仅仅能用工具补拙了。吐槽一下我们公司居然基本都在用source insight,这个工具中文编码支持又不好,搜索和补全命令不强,每次都仅仅能呵呵。
哪些把二进制数据也能玩得出神入化的大牛。我就不考虑了。对像我这种普通猿类,没能吃上ALZ112。更别提ALZ113了。智商有限。仅仅能用工具补拙了。吐槽一下我们公司居然基本都在用source insight,这个工具中文编码支持又不好,搜索和补全命令不强,每次都仅仅能呵呵。
- 单一目标的编辑。
这个在重构一书中也重复强调了。这里个人的体会是。老老实实的遵循吧,当习惯成自然了,进步的时机就到了。别以为学了高量和相对论,经典力学就能随便玩了。
这个在重构一书中也重复强调了。这里个人的体会是。老老实实的遵循吧,当习惯成自然了,进步的时机就到了。别以为学了高量和相对论,经典力学就能随便玩了。
2. 须要改动大量同样的代码
对改动同样代码,我近乎偏执。原因或许就是源于以下两句作者的话:
- 当你热情地消除代码中的反复时。就会吃惊地发现,设计会自己浮现出来。
- 消除反复是锤炼设计的强大手段。它不仅能让设计变得更灵活,还能令代码改动更快更easy。
这里补充一点的是同样的代码。不一定是全然同样的代码。有时出现全然同样的代码,仅仅是由于一种巧合;非常多时候,碰到最多的是结构和逻辑上相似的反复代码。
3. 时间紧迫。必须改动
不管时间是否紧迫,作为一种自我保护的本能。一般改动时,尽可能将改动的代码集中到单独的类或者方法中,实现上尽可能的是类似一种开关性质的,能够简单的enable or disable。
可是,假设时间充裕
。应该在可測的情况下进行可能的重构,我自己的感受是有时这样的自我保护的本能太过强烈,有些时候会有些畸形,这样写出来的代码或许是最安全的,但不是最优雅的。就像非常多贪心算法,不总是最优,但往往还够得上"优"
这里借用原书的样例讨论
原始的代码,对列表entries中的每一个对象,依次运行postDate()对象,然后加入的transactionBundle的管理池中。
public void postEntries(List<Entry> entries) {
for (Iterator<Entry> it =entries.iterator(); it.hasNext(); ) {
Entry entry = (Entry)it.next();
entry.postDate();
}
transactionBundle.getListManager().add(entries);
}
如今有个新的需求,需求描写叙述是这种:(需求描写叙述实际是非常关键的,不同的描写叙述方式会不自觉的影响程序猿的实现方式)
entries列表中不是全部的对象都要运行postDate()和加入进transactionBundle的管理池中。仅仅有还尚未在transactionBundle的管理池中的对象才须要运行postDate()操作,仅仅有那些运行了postDate()的entry对象,才须要加入到transactionBundle的管理池中。
依据上面的需求描写叙述。假设你是那99.9%的人,一般就会这样实现:
public voidpostEntries(List<Entry> entries) {
// 记录哪些entries中哪些对象运行了postDate()
List<Entry>entriesToAdd = newLinkedList<Entry>(); for (Iterator<Entry> it =entries.iterator(); it.hasNext(); ) {
Entry entry = (Entry)it.next();
// 仅仅有那些不在transactionBundle管理池中的entry对象才须要运行postDate()
if (!transactionBundle.getListManager().hasEntry(entry)) {
entry.postDate();
entriesToAdd.add(entry);
}
} // 将那些运行了postDate的entry对象加入到transactionBundle管理池中
transactionBundle.getListManager().add(entriesToAdd);
}
无疑,这种修改非常具有侵入性,一旦出错,非常难定位是本身已有的缺陷还是修改造成的——仅仅有在深入理解代码的修改逻辑之后才干分析错误原因。这个不好。
这个需求。本质上就是先找出那些还没有在管理池中的entry对象,然后运行postDate()和add()操作。因此这里实际能够应用“新生方法”手法,引入一个侵入性相当弱的改动。
public voidpostEntries(List<Entry> entries) {
// 先剔除那些已经在transactionBundle管理池中的entry对象
List<Entry> entriesToAdd =uniqueEntries(entries); for (Iterator<Entry> it = entriesToAdd.iterator();it.hasNext(); ) {
Entry entry = (Entry)it.next();
entry.postDate();
}
transactionBundle.getListManager().add(entriesToAdd);
}
// 剔除那些已经在transactionBundle管理池中的entry对象
private List<Entry> uniqueEntries(List<Entry> entries) {
// return entries; //假设出现错误,能够直接return。
// 新生方法的优点就是代码隔离。能够高速定位是改动引入的问题还是原始代码本身就有的bug List<Entry> result = new LinkedList<Entry>();
for (Iterator<Entry> it = entries.iterator(); it.hasNext(); ) {
Entry entry = (Entry)it.next();
if (!transactionBundle.getListManager().hasEntry(entry)) {
result.add(entry);
}
}
return result;
}
当然也能够引入外覆方法的手法。
外覆方法的第一步总是重命名原有方法和引入外覆方法,外覆方法名就是原有方法名。
这一步基本不会错。
//rename "postEntries(List<Entry> entries)" aspostEntriesDirectly
private voidpostEntriesDirectly(List<Entry> entries) {
for (Iterator<Entry> it =entries.iterator(); it.hasNext(); ) {
Entry entry = (Entry)it.next();
entry.postDate();
}
transactionBundle.getListManager().add(entries);
} // new wrapper method use signature "public voidpostEntries(List<Entry> entries)"
public voidpostEntries(List<Entry> entries) {
postEntriesDirectly(entries);
}
下一步,调整外覆方法的实现,这里基本与新生方法同样
// new wrapper method use signature "public voidpostEntries(List<Entry> entries)"
public voidpostEntries(List<Entry> entries) {
// 先剔除那些已经在transactionBundle管理池中的entry对象
List<Entry>entriesToAdd = uniqueEntries(entries); postEntriesDirectly(entriesToAdd);
}
假设习惯了思考使用弱侵入式的改动方式。后面两种方式会自然而然的得到。外覆方法与新生方法的差别是外覆方法保留了原有方法(仅仅是方法名做了改动)。
假设有须要,还能够新生类和外覆类。原理都差点儿相同。最后啰嗦一下。假设一開始需求是这样描写叙述的:
对entries列表中的Entry对象,首先要检查是否已经在管理池中。仅仅有不存在时才运行postDate()操作。并把它加入到管理池中。
这样描写叙述后。要想到后面两种方法就会更自然一些。
所以说需求描写叙述是非常关键。可是没人会为我们做这个,一切仅仅能靠自己。一切从需求分析開始。
读书报告之《改动代码的艺术》 (I)的更多相关文章
- 《修改代码的艺术》【PDF】下载
<修改代码的艺术>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382309 内容简介 <修改代码的艺术>针对大型的. ...
- springboot项目利用devtools实现热部署,改动代码自动生效
一.前言 spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能就是自动应用代码更改到最新的App上面去.原理是在发现代码有更改之后,重新启动应用,但是速度比手动停止后 ...
- jdgui反编译+javac编译=无源文件改动代码
首先我们要知道,打包好的Java程序中都是编译好的字节码文件(*.class).这些class文件会在执行的时候被载入到JVM中. 若想替换掉某一个类,那么仅仅须要将该类的源代码又一次编译然后再替换之 ...
- Spring Boot(四):利用devtools实现热部署,改动代码自动生效
一.前言 spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能就是自动应用代码更改到最新的App上面去.原理是在发现代码有更改之后,重新启动应用,但是速度比手动停止后 ...
- 代码可读性艺术在Andorid中的体现
前言 最近接手的一些项目,不同的人编码风格迥异,类里的变量.方法的定义穿插,注释极为稀少,更有一些变量和方法的命名非常近似,例如表示播放队列的"playQueue"和表示歌单的&q ...
- 软件project—思考项目开发那些事(一)
阅读文件夹: 1.背景 2.项目管理,质量.度量.进度 3.软件开发是一种设计活动而不是建筑活动 4.高速开发(简单的系统结构与复杂的业务模型) 5.技术人员的业务理解与产品经理的业务理解的终于业务模 ...
- maven自己主动编译,解决你每次代码改动须要又一次编译的繁琐
maven结构的项目,我们在每次改动代码后都会须要手动编译,以下命令能够解决此问题.仅仅要代码改动.会自己主动帮你编译. 进入项目文件夹运行:mvn -U eclipse:clean eclipse: ...
- 《Clean Code》 代码简洁之道
作者介绍 原文作者: Robert C. Martin, Object Mentor公司总裁,面向对象设计.模式.UML.敏捷方法学和极限编程领域的资深顾问,是<敏捷软件开发:原则.模式.与实践 ...
- 烂代码 git blame
关于烂代码的那些事(上) - Axb的自我修养 http://blog.2baxb.me/archives/1343 关于烂代码的那些事(上) 六月 21, 2015 57 条评论 目录 [显示] 1 ...
随机推荐
- 在 iSCSI Target 服务器中使用LVM创建和设置LUN(二)
LUN是逻辑单元号,它与iSCSI存储服务器共享.iSCSI 目标器通过TCP/IP网络共享它的物理驱动器给发起程序(initiator).这些来自一个大型存储(SAN:Storage Area Ne ...
- STL+Floyd【p1690】贪婪的Copy
Description Copy从卢牛那里听说在一片叫yz的神的领域埋藏着不少宝藏,于是Copy来到了这个被划分为个区域的神地.卢牛告诉了Copy这里共有个宝藏,分别放在第Pi个(1<=Pi&l ...
- 洛谷——P1706 全排列问题
P1706 全排列问题 题目描述 输出自然数1到n所有不重复的排列,即n的全排列,要求所产生的任一数字序列中不允许出现重复的数字. 输入输出格式 输入格式: n(1≤n≤9) 输出格式: 由1-n组成 ...
- [BZOJ 3108] 图的逆变换
Link: BZOJ 3108 传送门 Solution: 样例教你做题系列 观察第三个输出为No的样例,发现只要存在$edge(i,k),edge(j,k)$,那么$i,j$的出边一定要全部相同 于 ...
- POJ 3041 Asteroids (二分图匹配)
[题目链接] http://poj.org/problem?id=3041 [题目大意] 一个棋盘上放着一些棋子 每次操作可以拿走一行上所有的棋子或者一列上所有的棋子 问几次操作可以拿完所有的棋子 [ ...
- symbol(s) not found for architecture arm64
问题如下: 解决:更改环境 ok Standard architectures (armv7, arm7s)
- 用字符串连接SQL语句并用EXEC执行时,出现名称 '‘不是有效的标识符
原文:用字符串连接SQL语句并用EXEC执行时,出现名称 ''不是有效的标识符 用字符串连接SQL语句并用EXEC执行时,出现名称 '这里是字符串连接的一条SQL语句‘不是有效的标识符 才发现,在写e ...
- apache 配置防盗
防盗链目的:防止其他网站盗用自己的网站而增加额外的流量损失 SetEnvIfNoCase Referer "^http://.*\.yourdomin\.com" local_re ...
- 级联关系(内容大部分来自JavaEE轻量型解决方案其余的是我的想法)
1. 级联关系 在Hibernate程序中持久化的对象之间会通过关联关系互相引用.对象进行保存.更新和删除等操作时,有时需要被关联的对象也执行相应的操作,如:假设需要关联关系的主动方对象执行操作时,被 ...
- office2010使用mathtype时,出现未找到MathPage.WLL解决方案--亲测有用
安装mathtype时,出现如下错误: 解决方案: 参考此网址中的内容:http://www.mathtype.cn/wenti/word-jianrong.html 首先需要找到在Word加载的两个 ...
。应该在可測的情况下进行可能的重构,我自己的感受是有时这样的自我保护的本能太过强烈,有些时候会有些畸形,这样写出来的代码或许是最安全的,但不是最优雅的。就像非常多贪心算法,不总是最优,但往往还够得上"优"