过年回家了,在家里已经呆了两天,两天里陪父母烧锅,教他们使用红米手机,聊天,吃饭。家里很好,只是父母年纪又大了一岁,很心疼它们,听他们讲年后还要出去打工挣钱,不知道说什么,觉得很对不起他们。我真的是太没用了。父亲总是吸烟,烟瘾很重,一晚上要吸很多根,我很担心,但总是劝不停。两天没有总结这本刚看完的《重构》了,所以今晚上,虽然很冷,还是坐在椅子边上认真的敲一些字,总结一下吧。
重构 改善既有代码的设计第二章主要讲述的是重构的整体的含义、作用,主要是回答了重构的定义、重构的原因、重构何时进行,以及重构的难点,重构与性能之间的考量。
二话不说,先上总结的XMind。
这是本章的梗概。
使用XMind总结本章的主要内容,在各个小节可以看到如下的内容:
大体内容如上。
重构的定义主要有两种形式,一种名次形式,一种动词形式。
名次形式如下:
对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其理解性,降低其修改成本。
一般而言,重构都是对软件的小改动。诸如Extrac Class, Move Method, Move Field.
动词形式如下:
使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
需要强调的是,重构不会改变软件可观察的行为----重构之后软件功能一如往常。
两顶帽子:
重构和添加功能,两者关系如下:
添加新功能时,你不应该修改既有代码,只管添加新功能。重构时就不能再添加功能,只管改进程序结构
重构可以帮助程序员始终良好的控制自己的代码。重构可以并且应该用于以下几个目的
程序一般在不断的修改中,程序会逐渐失去自己的结构,程序员会越来越难以通过阅读源码而理解原来的设计,重构很像是整理代码
设计不良的程序往往需要更多代码,这常常是因为代码在不同的地方使用完全相同的语句做同样的事,因此改进设计的一个重要方向就是消除重复代码
如果消除重复代码,就可以确定所有事物和行为在代码中只表述一次,这正是优秀代码的设计。
应该稍微改变一下开发节奏,对代码做适当修改,让代码变得更易理解。
Kent Beck经常形容自己的一句话:“我不是个伟大的程序员,我只是个有着一些优秀习惯的好程序员。”
改善错误
提升可读性
减少错误
良好的设计是快速开发的根本,重构可以帮助你更快速地开发软件,因此它阻止系统腐败变质,甚至可以提高设计的质量。
几乎任何情况下都反对专门拨时间进行重构,重构应该随时随地进行
三次法则
第一次做某件事只管去做,第二次做类似的事情会产生反感,但无论如何还是可以去做;第三次再做,不,你应该进行重构
重构的时机
1. 添加功能时重构
1. 修补错误时重构
1. 复审代码时重构
如果能够得到别人的帮助,我的生活会滋润得多,所以我总是期待更多复审
用UML展现设计
重构为什么有用
两个问题
今天可以为你做什么?
明天可以为你做什么?
程序难以相与的原因
难以阅读的程序,难以修改
逻辑重复的程序,难以修改
添加新行为时需要修改已有代码的程序,难以修改
带复杂条件逻辑的程序,难以修改
良好的程序
容易阅读
所有的逻辑只在唯一地点指定
新的改动不会波及现有行为
尽可能简单表达条件逻辑
在Java编程思想中,Bruce Eckel说不要过早关注程序效率,这是个诱人的陷阱,最好是首先让程序运行起来,再考虑速度。
除了对性能有严格要求的实时系统,其他任何情况下,编写“快速软件”的秘密是:首先写出可调的软件,然后调整它以求获得足够的速度。
两种表述如出一辙。
public String statement() {
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental rental = (Rental) rentals.nextElement();
frequentRenterPoints += rental.getFrequentRenterPoints();
// show figures for this rental
result += "\t" + rental.getMovie().getTitle() + "\t"
+ String.valueOf(rental.getCharge()) + "\n";
totalAmount += rental.getCharge();
}
// add footer lines
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints)
+ " frequent renter points";
return result;
}
在第一章案例1中,去除临时变量之后可以看到statement()函数如下:
可以看到在处理getTotalCharge()和getTotalFrequentRenterPoints()函数过程中,使用了Replace Temp with Query两个函数,
public String statement() {
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += each.getFrequentRenterPoints();
// show figures for this rental
result += "\t" + each.getMovie().getTitle() + "\t"
+ String.valueOf(each.getCharge()) + "\n";
}
// add footer lines
result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
result += "You earned " + String.valueOf(getTotalFrequentRenterPoints())
+ " frequent renter points";
return result;
}
在这个过程中使用了类似的重构,即由于totalAmount和frequentRenterPoints均位于循环内部赋值,所以在重构过程中不得不把循环复制到查询函数中。
// 译注:此即所谓query method
private int getTotalFrequentRenterPoints() {
int result = 0;
Enumeration rentals = _rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += each.getFrequentRenterPoints();
}
return result;
}
可以看到这次重构存在一个问题,即性能,
原本代码只执行while循环一次,新版本要执行三次,如果while耗时很多,就可能大大降低程序的性能。单单为了这个原因,很多程序员就不愿进行这个重构动作。但是请注意我的用词,”如果”和“可能”。除非我进行评测,否则我无法确定循环的执行时间,也无法知道这个循环是否被经常使用以至于影响系统的整体性能。重构时你不必担心这些,优化时你才需要担心它们,但那时候你已经处于一个比较有利的位置,有更多的选择可以完成有效优化。
在重构这本书中,这一章主要阐明了重构的原因、时机以及重构与性能之间的问题和关系。尤其重构定义,最好能够背诵并深刻理解,这样才能真正的理解重构原理。
《重构 改善既有代码的设计》之重构原则
《重构 改善既有代码的设计》之重构,第一个案例详解
《重构 改善既有代码的设计》之JUnit测试框架以及IDEA与JUnit整合
《重构 改善既有代码的设计》之代码的坏味道
《重构 改善既有代码的设计》之重构列表