《重构 改善即有代码的设计》笔记

我不是个伟大的程序员,我只是个有着一些优秀习惯的好程序员。--Kent Beck

个人认为这本书是每个程序员都需要读的一本书,有种让人欲罢不能的感觉。有人把《重构》与《设计模式》并列为“java行业的圣经”。

下面是我提炼出来的一些观点或者是总结。首先需要认识重复代码是软件万恶之源,这些重复代码应该被抽出来放进同一个函数中。重复代码是系统中最糟糕的东西之一。正因为如此,我们才要重构才需要有设计模式去消除它。并且代码的可理解特性应该是我们虔诚追求的目标。


设计模式为重构提供了目标。以重构方式改进软件质量,本质上说,重构就是在代码写好之后改进它的设计。最好是由老资格、经验丰富的开发人员来引人重构的技术,因为这样的人最能够透彻理解重构背后的原理,并根据情况加以调整,使之适应于特定工作领域。


重构:对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。


为何重构:

重构改进软件设计

重构使软件更容易理解

重构帮助找到bug

重构提高编程速度


何时重构:

1)事不过三,三则重构:第一次做某事时只管去做;第二次做类似的事会产生反感,但无论如何还是可以去做;第三次再做类似的事,就应该重构。

2)添加功能时重构

3)修补错误时重构

4)复审代码时重构。


1、每个类都应该有个接口或者抽象类。即所谓的面向接口编程


2、在设计模式中,所谓的“实现一个接口”并不一定表示写一个类,并利用implement关键词来实现这个java接口。“实现一个接口”泛指“实现某个超类型(可以是类或接口)的某个方法”。


3、大多数重构都为程序引人了更多间接层。重构往往把大型对象拆成多个小型对象,把大型函数拆成多个小型函数。间接层的某些价值:

允许逻辑共享;

分开解释意图和实现;

隔离变化

封装条件逻辑。


4、和重构一样,性能优化通常不会改变组建的行为(除了执行速度),只会改变其内部结构。但两者出发点不同:性能优化往往使代码较难理解,但为了得到所需要的性能不得不那么做。


5、进行重构的时候,我们需要依赖测试,让它告诉我们是否引入bug。好的测试是重构的根本。


6、代码块越小,代码的功能就愈容易管理。代码的处理和移动也就越轻松。


7、任何不会被修改的变量都可以被当成参数传入新的函数,至于会被修改的变量就需要格外小心。如果只有一个变量会被修改,可以把它当作返回值。


8、绝对值得更改一个变量的名称。重构一个函数时return的变量定义为result。


9、尽量除去临时变量。临时变量往往引发问题,它们会导致大量参数被传来传去,而其实完全没有这个必要。当然也可能会付出性能上的代价。


10、switch可被状态模式替换。大多数的时候一看到switch就应该考虑以多态来替换它。


11、修改接口:

尽量这么做:让旧接口调用新接口。

除非真有必要,不要发布接口。


12、过长的函数:

需要遵循这样的一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说的东西写进一个独立函数中,并以其用途命名。


13、条件表达式和循环常常也是提炼的信号。循环,应该将循环和其内的代码提炼到一个独立函数中。


14、过大的类:

如果想利用单个类做太多的事情,其内往往就会出现太多实例变量。也是代码重复、混乱并最终走向死亡的源头。应该提炼一些类。


15、面向对象程序中的函数,其参数列通常比在传统程序中短得多 


16、对象技术的全部要点在于:

这是一种“将数据和对数据的操作行为包装在一起”的技术。


17、一个函数往往会用到几个类的功能,那么TA究竟该被置于何处呢?

原则是:判断哪个类拥有最多被此函数使用的数据,然后就把这个函数和那些类摆在一起。


18、所有的超类都应该是抽象的。


19、类应该包含它们自己的测试代码:

每个类都应该有一个测试函数,并以它来测试自己这个类。


20、一套测试就时一个强大的bug侦测器,能够大大缩减查找bug所需要的时间。


21、模式和重构之间有着一种与生俱来的关系。模式时你希望到达的目标,重构则是到达之路。


22、提炼函数:

简短而命名良好的函数的优点:

首先,如果每个函数的粒度都很小,那么函数被复用的机会就更大;

其实,这会使高层函数读起来就像一系列注释;

再次,如果函数都是细粒度,那么函数的覆写也会更容易些。


23、如果临时变量并未被声明final,那就将它声明为final。

临时变量会使代码难以被提炼,所以应该尽可能把它们替换为查询式。


24、在java中,不要对参数赋值:如果看到手上的代码已经这样类,就是用移除对参数的赋值。


25、类往往会因为承担过多责任而变得臃肿不堪。这种情况可以将一部分责任分离出去。“搬移函数”是重构理论的支柱。


26、如果发现对于一个字段,在其所驻类之外的另一个类中有更多函数使用类它,就应该 考虑搬移这个字段。所谓“使用”可能是通过设置/取值函数间接进行的。


27、一个类应该是一个清楚的抽象,处理一些明确的责任。


28、封装意味每个对象都应该尽可能少了解系统的其他部分。如此一来,一旦发生变化,需要了解系统的其他部分。如此一来,一旦发生变化,需要了解这一变化的对象就会比较少—这会使变化比较容易进行。

随着试验日渐丰富,你会发现,有更多可以(而且值得)封装的东西。


29、间接访问变量的好处时,子类可以通过覆写一个函数而改变获取数据的途径;它还支持更灵活的数据管理方式,例如延迟初始化。


30、如果一个字面数值带有特别含义,创建一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量。


31、类中如果存在一个public字段。将它声明为private,并提供相应的访问函数。


32、有一个函数返回一个集合。让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数。


33、你有一个不可变的类型码,它会影响类的行为。以子类取代这个类型码。


34、你有一个类型码,它会影响类的行为,但你无法通过继承手法消除它。以状态对象取代类型码。


35、建立子类的目的,是为了增加新特性或变化其行为。


36、在面向对象术语中,听上去最高贵的词非“多态”莫属。多态最根本的好处就是:

如果你需要根据对象的不同类型而采取不同的行为,多态使你不必编写明显的条件表达式。正因为有了多态,所以你会发现;“类型码的switch语句”以及“基于类型名称的if-then-else语句”在面向对象程序中很少出现。


37、良好的接口只向用户展现必须展现的东西。如果一个接口暴露类过多的细节,你可以将不必要暴露的东西隐藏起来,从而改进接口的质量。毫无疑问,所有数据都应该隐藏起来,同时,所有可以隐藏的函数都应该被隐藏起来。


38、过长的参数列是不好的味道,因为程序员很难记住那么多参数,而且长参数列往往伴随着坏味道。


39、某个函数既返回对象状态值,又修改对象状态。建立两个不同的函数,其中一个负责查询,另一个负责修改。


40、你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。改为传递整个对象。


41、某些参数总是很自然地同时出现,以一个对象取代这些参数。


42、所有字段都是final,这样可以避免别名带来的困扰。


43、不希望在对象创建之后,此字段还有机会被改变,那就不要为它提供设置函数(同时将该字段设为final)


44、有一个函数,从来没有被其他任何类用到。将这个函数修改为private。


45、你希望在创建对象时不仅仅时做简单的建构动作,将构造函数替换为工厂函数。


46、向下转型在java特别盛行,因为java没有模板机制,因为如果你想从集合之中取出一个对象,就必须进行向下转型。某个函数返回的对象,需要由函数调用着执行向下转型(downcast)。将向下动作移到函数中。


47、某个函数返回一个特定的代码,用以表示某种错误情况,改用异常。


48、两个子类拥有相同的字段。将该字段移至超类。

有些函数,在各个子类中产生完全相同的结果。将该函数移至超类。

你在各个子类中拥有一些构造函数,它们的本体几乎完全一致。在超类中新建一个构造函数,并在子类构造函数中调用它。很多时候,子类构造函数的唯一动作就是调用超类构造函数。


49、两个类有想似特性,为这两个建立一个超类,将相同特性移至超类。


50、若干客户使用类接口中的同一子集,或者两个类的接口有部分相同。将相同的子集提炼到一个独立接口中。


51、超类和子类之间无太大区别。将它们合为一体。


52、你有一些子类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上有所不同。

将这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了。然后将原函数上移至超类。


53、继承是避免重复行为的一个强大工具。可以明显减少子类中的代码量。


54、你常常会遇到这样的情况:一开始继承类一个类,随后发现超类中的许多操作并不真正适用于子类。这种情况下,你所拥有的接口并未真正反映出子类的功能。或者你可能发现你从超类中继承了一大堆子类并不需要的数据,抑或你可能发现超类中的某些protected函数对子类并没有什么意义。

如果以委托取代继承,你可以更清楚的表明:你只需要受委托类的一部分功能。接口中的某一部分应该被使用,哪一部分应该被忽略,完全有你主导控制。这样做的成本则是需要额外写出委托函数,但这些函数都非常简单,极少出错。


55、某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。

在子类中新建一个字段用以保存超类;调整子类函数,令它改而委托超类;然后去掉两者之间的继承关系。


56、你在两个类之间使用委托关系,并经常为整个接口编写许多极简单的委托函数。让委托类继承受委托类。


57、将过程化设计转化为对象设计:

将数据记录变成对象,将大块的行为分成小块,并将行为移入相关对象之中。


类代码行数不超过400行,函数行数不超过20,嵌套不超过3层。设计模式不是软件设计的起点,而是终点。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值