重构改善既有代码的设计
ptn3900
人生三大乐趣:写代码,车,股票
展开
-
3.21 refused bequest
如果子类复用类超的行为,却又不愿意支持超类的接口。应该运用replace inheritance with delegation达到目的。原创 2011-10-20 22:13:47 · 478 阅读 · 0 评论 -
7.5 hide delegate (隐藏委托关系)
客户通过一个委托类来调用另一个对象。在服务类上建立客户所需的所有函数,用以隐藏委托关系。动机:封装意味每个对象都应该尽可能少了解系统的其他部分。如此一来,一旦发生变化,需要了解这一变化的对象就会比较少。如果某个客户先通过服务对象的字段得到另一个对象,然后调用后者的原创 2011-10-19 10:02:41 · 1026 阅读 · 0 评论 -
3.15 message chains (过度耦合的消息链)
一个对象请求另一个,后者在请求下一个对象,....这就是消息链。采取这种方式,意味客户代码将与查找过程中的导航结构紧密耦合,一旦对象间的关系发生任何变化,客户端就不得不做出相应修改。这时候应该使用hide delegate。通常更好的手法:先观察消息链最终得到的对象是用来原创 2011-10-19 09:50:05 · 1454 阅读 · 0 评论 -
3.14 temporary field (令人迷惑的暂时字段)
某个对象的实例变量仅为某种特定情况而设。在变量未被使用的情况下猜测当初其设置目的,会让你发疯。请使用extract class给这些变量创造一个家。如果类中有一个复杂算法,需要好几个变量,往往就可能导致temporary field,由于实现者不希望传递一长串参数,所以他原创 2011-10-19 09:43:33 · 1150 阅读 · 0 评论 -
3.13 speculative generality (夸夸其谈未来性)
如果所有装置都会被用到,那就值得那么做,如果用不到,就不值得。用不上的装置只会挡你的路,所以,把它搬开。如果某个抽象类其实没有太大作用,请运用collapse hierarchy,不必要的委托可运用inline class除掉。如果函数的某些参数未被用上,实施remove p原创 2011-10-19 09:35:56 · 697 阅读 · 0 评论 -
11.9 collapse hierarchy (折叠继承体系)
超类和子类之间无太大区别。将它们合为一体。动机:继承体系很容易变的过分复杂。所谓重构继承体系,往往是将函数和字段在体系中上下移动。完成这些动作后,很可能发现某个子类并未带来该有的价值,因此需要把超类与子类合并起来。做法:选择想移除的类,是超类还是子类。使用原创 2011-10-19 09:30:52 · 1094 阅读 · 0 评论 -
3.12 lazy class(冗赘类)
你所创建的每一个类,都得有人去理解它,维护它,这些工作都是要花钱的。如果一个类的所得不值其身价。它就应该消失。某个类原本对得起自己的身价,但重构使它身形缩水,不再做那么多工作;或开发者事前规划了某些变化,并添加一个类来应付这些变化,但变化实际上没有发生。如果某些子类没有工原创 2011-10-19 09:26:06 · 685 阅读 · 0 评论 -
3.11 parallel inheritance hierarchies (平行继承体系)
如果为某个类增加一个子类,必须也为另一个类相应增加一个子类。如果发现某个继承体系的类名称前缀和另一个继承体系的类名称前缀完全相同,便是这种坏味道。策略:让一个继承体系的实例引用另一个继承体系的实例。如果再接再励使用move method和move field,就可以将引用端的原创 2011-10-19 09:20:49 · 1430 阅读 · 0 评论 -
3.10 witch statements(switch 惊悚现身)
面向对象程序的一个最明显特征是:少用switch语句。switch语句常常根据类型码进行选择,所以应该使用extract method将switch语句提炼到一个独立函数中,再以move method将它搬移到需要多态性的那个类里。此时决定是否使用replace type c原创 2011-10-18 10:46:30 · 775 阅读 · 0 评论 -
9.7 introduce Null object (引入null对象)
需要再三检查对象是否为null。将null值替换为null对象。动机:当某个字段是null时,多态可以扮演另一个较不直观的用途。控对象一定是常量,它们的任何成分都不会发生变化。做法:为源类建立一个子类,使其行为就像是源类的null版本。在源类和null子类原创 2011-10-18 15:16:56 · 887 阅读 · 0 评论 -
10.6 replace parameter with explicit methods(以明确函数取代参数)
有一个函数,完全取决于参数值而采取不同行为。针对该参数的每一个可能值,建立一个独立函数。动机:如果某个参数有多种可能的值,而函数内又以条件表达式检查这些参数值,并根据不同参数值做出不同的行为,那么就应该使用本项重构。如果以参数决定函数行为,那么函数用户不但需要观察该函原创 2011-10-18 14:42:05 · 631 阅读 · 0 评论 -
9.6 replace conditional with polymorphism (以多态取代条件表达式)
条件表达式,根据对象类型的不同而选择不同的行为。将这个条件表达式的每一个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。动机:多态使你不必编写明显的条件表达式。如果同一组条件表达式在程序许多地方出现,那么使用多态的收益是最大的。做法:使用re原创 2011-10-18 11:14:23 · 1807 阅读 · 0 评论 -
6.4replace temp with query
你的程序以一个临时变量保存某一表达式的运算结果。动机:临时变量的问题在于:它们是暂时的,而且只能在所属函数内使用。由于临时变量只在所属函数内可见,所以它们会驱使你写出更长的函数,因为只有这样才能访问到需要的临时变量。如果把临时变量替换为一个查询,那么同一个类中的所有函数都原创 2011-10-10 10:41:57 · 274 阅读 · 0 评论 -
3.2long method
每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途命名。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也毫不犹豫地那么做。关键不在于函数的长度,而在于函数“做什么”和“如何做”之间的语义距离。如果函数内有大量的原创 2011-10-10 10:19:28 · 312 阅读 · 0 评论 -
3.16 Middle Man(中间人)
过度使用委托,某个类的接口有一半的函数都委托给其他类,这样就是过度运用。应该使用remove middle man,直接和真正负责的对象打交道。如果这样的函数只有少数几个,可以运用inline method把它们放进调用端。如果这些middle man还有其他行为,可以运用rep原创 2011-10-19 10:06:55 · 447 阅读 · 1 评论 -
7.6 remove middle man(移除中间人)
某个类做了过多的简单委托动作。让客户直接调用受委托类。动机:随着受托类的特性越来越多,这一过程让你痛苦不已,服务类完全变成一个中间人,此时应该让客户直接调用受托类。做法:建立一个函数,用以获得受托对象。对于每个委托函数,在服务类中删除该函数,并让需要调用该原创 2011-10-19 10:20:04 · 466 阅读 · 0 评论 -
11.7 extract superclass (提炼超类)
两个类有相似特性为这两个类新建一个超类,将相同特性移至超类。动机:两个类以相同的方式做类似的事情,或者以不同的方式做类似的事情。做法:为原本得类新建一个空白的抽象超类。运用pull up field,pull up method和pull up constructor body逐一将子类的共同元素上移到超类。检查留在子类中的函数,看它们是否还有共通成分。如果有,可以使用原创 2011-10-20 21:10:34 · 721 阅读 · 0 评论 -
3.18 alternative classes with different interfaces(异曲同工的类)
如果两个函数做同一件事,却有着不同的签名,请运用rename method根据他们的用途重新命名。或许可以使用extract superclass。原创 2011-10-20 20:51:29 · 463 阅读 · 0 评论 -
7.7 introduce foreign method(引入外加函数)
需要为提供服务的类增加一个函数,但你无法修改这个类。动机:添加外加函数实现无法修改服务类的新功能。做法:在客户类中建立一个函数,用来提供你需要的功能。=》这个函数不应该调用客户类的任何特性。如果它需要一个值,把该值当作参数传给它。以服务类实例作为该函数的第一个参数。旧代码Date newStart = new Date(previousend.getYear(),原创 2011-10-20 21:35:17 · 653 阅读 · 0 评论 -
9.8 introduce assertion
某一段代码需要对程序状态做出某种假设。动机:使用断言明确标记假设。断言是一个条件表达式,应该总是真,如果失败,表示程序员犯了错误。做法:如果发现代码假设某个条件始终为真,就加入一个断言明确说明这种情况。断言用来检查“一定必须为真”的条件。原创 2011-10-20 22:22:12 · 514 阅读 · 0 评论 -
3.22 comments (过多的注释)
如果需要注释来解释一块代码,试试extract method如果函数已经提炼出来,还需要注释,试试rename method。如果还需要注释说明某些系统规格,试试introduce assertion原创 2011-10-20 22:15:51 · 431 阅读 · 0 评论 -
3.20 Data class(纯稚的数据类)
拥有一些字段,以及用于访问这些字段的函数,除此之外一无长物。这样的类只是一种不会说话的数据容器,它们几乎一定被其他类过份细琐的操控着。使用encapsulate field封装起来。使用encapsulate collection把它们封装起来。对于那些不该被其他类修改的字段,请运用remove setting method。搬移相关函数后,运用hide method。原创 2011-10-20 22:07:56 · 690 阅读 · 0 评论 -
7.8 Introduce local extension (引入本地扩展)
需要为服务类提供一些额外函数,但你无法修改这个类。建立一个新类,使它包含这些额外函数。让这个扩展品成文源类的子类或包装类。动机:所谓本地扩展是一个独立的类,但也是被扩展类的子类型:它提供源类的一切特性,同时额外添加新特性。在任何使用源类的地方,都可以使用本地扩展取而代之。如果有其他对象引用了就对象,就同时有两个对象保存了原数据。做法:建立一个扩展类,将它作为原始类的子类或包原创 2011-10-20 21:47:22 · 633 阅读 · 0 评论 -
3.19 Incomplete library class(不完美的库类)
如果你只想修改库类的一两个函数,可以运用introduce foreign method;如果想要添加一大堆额外行为,就得运用introduce local extension。原创 2011-10-20 21:25:04 · 1416 阅读 · 0 评论 -
11.11 replace inheritance with delegation(以委托取代继承)
某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。在子类中新建一个字段用以保存超类;调整子类函数,令它改而委托超类;然后去掉两者之间的继承体系。动机:一开始继承了一个类,随后发现超类中的许多操作并不真正适用于子类。这种情况下,你所拥有的接口并未真正反映出原创 2011-10-19 11:29:09 · 1070 阅读 · 1 评论 -
8.8 change bidirectional association to unidirectional (将双向关联改为单向关联)
两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性。动机:双向关联很有用,但你必须为它付出代价,那就是维护双向连接。大量的双向连接也很容易造成“僵尸对象”。双向关联也迫使两个类之间有了依赖:对其中一个类的任何修改,都可能引发另一个类的变化。做法:原创 2011-10-19 10:59:00 · 1381 阅读 · 0 评论 -
3.17 inappropriateintimacy (狎昵关系)
有时会看到两个类过于亲密,花费太多时间去探究彼此的private成分。可以采用move method和move field划清界限。可以运用change bidrectional association to unidirectional让其中一个类对另一个类斩断情丝。如果情原创 2011-10-19 10:39:19 · 954 阅读 · 0 评论 -
6.2 inline method (内联函数)
一个函数的本体与名称同样清楚易懂。在函数调用点插入函数本体,然后移除该函数。动机:某些函数,其内部代码和函数名称同样清晰易懂。可以去掉该函数。手上有一群组织不合理的函数,将它们都内联到一个大型函数中,再从中提炼出组织合理的小型函数。如果别人使用了太多间接层,使原创 2011-10-19 10:28:45 · 1006 阅读 · 0 评论 -
7.3extract class(提炼类)
某个类做了应该由两个类做的事。建立一个新类,将相关的字段和函数从旧类搬移到新类。动机:如果某些数据和某些函数总是一起出现,某些数据经常同时变化甚至彼此相依,这就表示你应该将它们分离出去。如果子类化只影响类的部分特性,或如果发现某些特性需要以一种方式来子类化,某些特原创 2011-10-08 16:43:31 · 564 阅读 · 0 评论 -
6.9substitute algorithm(替换算法)
动机:把某个算法替换为一个更清晰的算法。做法:准备好另一个算法,让它通过编译。针对现有测试,执行上述的新算法。如果结果相同,重构结束。如果不同,进行新旧比照。原创 2011-10-08 16:31:55 · 688 阅读 · 2 评论 -
11.10form template method(塑造模板函数)
有一些子类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上有所不同。将这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了。然后将原函数上移至超类。动机:两个函数以相同顺序执行大致相近的操作,但是各操作不完全相同。这种情况可以原创 2011-10-08 16:27:57 · 573 阅读 · 0 评论 -
3.6 shotgun surgery(散弹式修改)
如果没遇到某种变化,都必须在许多不同的类内做出许多小修改,你所面临的坏味道就是shotgun surgery。应该使用move method和move field把所有需要修改的代码放进同一个类。如果眼下没有合适的类可以安置这些代码,就创造一个。通常可以运用inline cl原创 2011-10-16 14:42:32 · 1378 阅读 · 1 评论 -
10.8 replace parameter with methods(以函数取代参数)
动机:如果函数可以通过其他途径获得参数值,那么它就不应该通过参数取得该值。过长的参数列会增加程序阅读者的理解难度,因此我们应该尽可能缩短参数列的长度。方法一:看看参数接收端是否可以通过与调用端相同的计算来取得参数值。如果调用端通过其所属对象内部的另一个函数来计算参数,并在原创 2011-10-16 10:59:29 · 531 阅读 · 0 评论 -
3.4 long parameter list(过长参数列)
太长的参数列难以理解,太多的参数会造成前后不一致,不易使用,而且一旦你需要更多数据,就不得不修改它。如果将对象传递给函数,大多数修改都将没有必要,因为你很可能只需增加一两条请求,就能得到更多数据。如果向已有的对象发出一条请求就可以取代一个参数,那么你应该激活重构手法repla原创 2011-10-16 10:24:41 · 766 阅读 · 0 评论 -
11.6 extract subclass 提炼子类
类中的某些特性只被某些实例用到。新建一个子类,将上面所说到的那一部分特性移到子类中。动机:发现类中的某些行为只被一部分实例用到,其他实例不需要它们。有时候这种行为上的差异是通过类型码区分的,此时可以使用replace type code with subclasses原创 2011-10-14 17:35:54 · 434 阅读 · 0 评论 -
9.1 decompose conditional(分解条件表达式)
动机程序之中,复杂的条件逻辑是最常导致复杂度上升的地点之一。必须编写代码来检查不同的条件分支,根据不同的分支做不同的事。然后,你很快就会得到一个相当长的函数。和任何大块头代码一样,你可以将它分解为多个独立函数,根据每个小块代码的用途,为分解而得的新函数命名,并将原函数中对原创 2011-10-13 11:34:12 · 464 阅读 · 0 评论 -
8.6 duplicate observed data(复制“被监视数据”)
你有一些领域数据置于GUI控件中,而领域函数需要访问这些数据。将该数据复制到一个领域对象中。建立一个observer模式;用以同步领域对象和GUI对象内的重复数据。动机:一个分层良好的系统,应该将处理用户界面和处理业务逻辑的代码分开。尽管可以轻松地将“行为”划分到原创 2011-10-16 10:22:40 · 564 阅读 · 0 评论 -
10.9introduce parameter object
某些参数总是很自然地同时出现。以一个对象取代这些参数。动机:常会看到特定的一组参数总是一起被传递。可能有好几个函数都使用这一组参数,这些函数可能隶属同一个类,也可能隶属不同的类。这就是data clumps(数据泥团),可以运用一个对象包装所有这些数据,再以该对象取代原创 2011-10-10 11:35:55 · 1232 阅读 · 0 评论 -
第三章代码的坏味道
3.1Duplicated Code(重复代码)如果你在一个以上的地点看到相同的程序结构,那么可以肯定:设法将它们合而为一,程序会变得更好。同一个类的两个函数含有相同的表达式。两个互为兄弟的子类内含相同表达式。两个毫不相关的类出现重复代码。3.2long me原创 2011-10-03 23:19:41 · 580 阅读 · 0 评论 -
第2章重构原则
*重构(名词):对软件内部的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。*重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。两顶帽子:1.添加新功能2.重构2.2为何重构1.重构改进软件设计原创 2011-10-03 22:21:44 · 387 阅读 · 0 评论