1. Self Encapsulate Field(自封装值域)算法
你直接访问一个值域,但与值域之间的耦合关系逐渐变得笨拙。为这个值域创建取值/设值函数(get/set),而且只以这些函数来访问值域。数据库
应用场景:若是你想访问superclass中的一个值域,却又想在subclass中将[对这个变量的访问]改成一个计算后的值,这就是最该使用Self Encapsulate Field(171)的时候。[值域自我封装]只是第一步。完成自我封装以后,你能够在subclass中根据本身的须要随意覆写取值/设值函数(getting/setting methods)。编程
示例:数组
private int _low, _high数据结构
boolean includes (int arg) {框架
return arg >= _low && arg <= _high;dom
}编程语言
重构为:模块化
private int _low, _high函数
boolean includes (int arg) {
return arg >= getLow() && arg <= getHigh();
}
int getLow() {return _low};
int getHigh() {return _high};
2. Replace Data Value with Object(以对象取代数据值)
将一些数据按照必定的规则或特征整合到一个对象中
应用场景:开发代码初期,仅须要一些简单的数据,好比一我的的基本信息:姓名,身份证号码,随着时间的推移,还须要其余的数据:手机号,地址,学历等等,这样就会罗列一排的变量,每次处理一我的的数据是,要传入不少参数。此时将这我的的信息就能够规整到一个类里面,仅声明一个对象便可,并且随着这我的信息的持续丰富,不会每次都冲击到原有的接口。
3. Change Value to Reference(将实值对象改成引用对象)
你有一个class,衍生出许多相等的实体,你但愿将他们替换为单对象。将这个实值对象变成一个引用对象。
应用场景:咱们通常使用一个类的时候都是须要了就new一个对象出来,这些每次new出来的对象就是实值对象,但不少场景下咱们会改变这些对象的内部状态,而且须要对这些对象进行传递,此时用实值对象来知足这个诉求就会显得很是笨拙,好比new出来一个car,而后设置下品牌是宝马,设置下颜色是蓝色等等。这种场景下咱们就须要一个引用对象,同一个类中能够是成员变量,不一样的类中使用可使用相似工厂、单例等模式来有一个对象管理类来供其余类取用。
4. Change Reference to Value(将引用对象改成实值对象)
和3相反。
应用场景:实值对象一个重要特色是保持不变,这样能够保证每一个使用者不用担忧数据的变化和同步。引用对象每每会让逻辑变得复杂,因此使用的时候要作好选择。
5. Replace Array with Object(以对象取代数组)
以对象替换数组,对于数组中的每一个元素,以一个值域表示。
应用场景:数组是一种常见的用于组织数据的结构体。不过它们应该只用于以某种顺序容纳一组类似对象。有时候你会发现,一个数组容纳了数种不一样对象,这会给数组用户带来麻烦,由于它们很难记住像“数组的第一个元素是人名“这样的约定。对象就不同。能够经过命名和函数来传递一些信息。让使用者更容易理解。
示例:
String[] row = new String[3];
row[0] = "Liverpool";
row[1] = "15";
重构为:
Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");
6. Duplicate Observed Data(赋值“被监视数据”)
你有一笔domain data置身于GUI空间中,而domain method须要访问它们。将这笔数据拷贝到一个domain object中,创建一个Observer模式,用意对domain object和GUI object内的重复数据进行同步控制。
应用场景:一个分层良好的系统,应该将处理用户界面和处理业务逻辑的代码分开。之因此这样作,缘由有一下几点:(1)你可能须要使用不一样的用户界面来表现相同的业务逻辑,若是同时承担两种责任,用户界面会变得过度复杂;(2)与GUI隔离后,领域对象的维护和演化都会更容易,甚至可让不一样的开发者负责不一样部分的开发。尽管能够轻松地将“行为”划分到不一样部位,“数据”却每每不能如此。同一项数据有可能既须要内嵌于GUI控件,也须要保存于领域模型里。自从MVC模式出现后,用户界面框架都是用多层系统来提供某种机制,使你不但能够提供这类数据,并保持他们同步。若是代码是以两层方式开发,业务逻辑被内嵌于用户界面之中,就有必要将行为分离出来。其中的主要工做就是函数的分解和搬移。但数据就不一样了:不能仅仅只是移动数据,必须将它复制到新对象中,并提供相应的同步机制。
7. Change Unidirectional Association to Bidirectional(将单项关联改成双向)
两个classes都须要使用对方特性,但其间只有一条单向链接。添加一个反向指针,并使修改函数可以同时更新两条链接。
应用场景:开发初期,你可能会在两个classes之间创建一条单向链接,使其中一个class能够引用另外一个class。随着时间的推移,你可能发现被引用的class须要获得其引用者来进行某些处理,也就是说它须要一个反向指针。
8. Change Bidirectional Association to Unidirectional(将双向关联改成单向)
应用场景:双向关联颇有用,但你也必须为它付出代价,那就是“维护双向链接,确保对象被正确的建立和删除”而增长的复杂度。因此只有在你须要双向关联的时候,才应该使用它。若是你发现双向关联再也不有存在价值,就应该去掉其中没必要要的一条关联。
9. Replace Magic Number with Symbolic Constant(以符号常量/字面常量取代魔法数字)
应用场景:在计算科学中,魔法数(magic number)是历史最悠久的不良现象之一。所谓魔法数是指拥有特殊意义,却又不能明确表现出这种意义的数字。若是你须要在不一样的地点引用同一个逻辑数,魔法数会让你烦恼不已,由于一旦这些数发生改变,你就必须在程序中找到全部魔法数,并将它们所有修改一遍,这简直就是一场噩梦。就 算你不须要修改,要准确指出每一个魔法数的用途,也会让你颇费脑筋。许多语言都容许你声明常量。常量不会形成任何性能开销,却能够大大提升代码的可读性。进行本项重构以前,你应该先寻找其余替换方案。你应该观察魔法数如何被使用,然后每每你会发现一种更好的使用方式。若是这个魔法数是个type code(型别码), 请考虑使用Replace Type Code with Class;若是这个魔法数表明一个数组的长度,请在遍历该数组的时候,改用Array.length()。
10. Encapsulate Field(封装值域)
能理解和第一条“自封装值域”的区别吗?
应用场景:面向对象的首要原则之一就是封装(encapsulation),或者称为「数据隐藏」(data hidding)。按此原则,你毫不应该将数据声明为public,不然其余对象就有可能访问甚至修改这项数据,而拥有该数据的对象却毫无察觉。这就将数据和行为分开了(不妙)。public 数据被看作是一种很差的做法,由于这样会下降程序的模块化程度(modularity)。若是数据和使用该数据的行为被集中在一块儿,一旦状况发生变化,代码的修改就会比较简单,由于须要修改的代码都集中于同一块地方,而不是星罗棋布地散落在整个程序中。Encapsulate Field是封装过程的第一步。经过这项重构手法,你能够将数据隐藏起来,并提供相应的访问函数(accessors)。但它毕竟只是第一步。若是一个class除了访问函数(accessors)外不能提供其余行为,它终究只是一个dumb class (哑类〕。这样的class并不能得到对象技术的优点,而你知道,浪费任何一个对象都是很很差的。实施Encapsulate Field以后,我会尝试寻找那些使用「新建 访问函数」的函数,看看是否能够经过简单的Move Method 轻快地将它们移到新对象去。
11. Encapsulate Collection(封装群集)
应用场景:class经常会使用群集(collection,多是array、list、set或vector)来保存一组实体。这样的class一般也会提供针对该群集的「取值/设值函数」(getter/setter)。可是,群集的处理方式应该和其余种类的数据略有不一样。取值函数(getter)不应返回群集自身,由于这将让用户得以修改群集内容而群集拥有者却一无所悉。这也会对用户暴露过多「对象内部数据结构」的信息。若是一个取值函数(getter)确实须要返回多个值,它应该避免用户直接操做对象内所保存的群集,并隐藏对象内「与用户无关」的数据结构。至于如何作到这一点,视你使用的版本不一样而有所不一样。另外,不该该为这整个群集提供一个设值函数(setter),但应该提供用觉得群集添加/移除(add/remove)元素的函数。这样,群集拥有者(对象)就能够控制群集元素的添加和移除。若是你作到以上数点,群集(collection)就被很好地封装起来了,这即可以下降群集拥有者(class)和用户之间的耦合度。
12. Replace Record with Data Class(以数据类取代记录)
应用场景:Record structures (记录型结构)是许多编程环境的共同性质。有一些理由使它们被带进面向对象程序之中:你可能面对的是一个老旧程序( legacy program ),也可能须要经过一个传统API 来与structured record 交流,或是处理从数据库读出的 records。这些时候你就有必要建立一个interfacing class ,用以处理这些外来数据。最简单的做法就是先创建一个看起来相似外部记录(external record)的class ,以便往后将某些值域和函数搬移到这个class 之中。一个不太常见但很是使人注目的状况是:数组中的每一个位置上的元素都有特定含义,这种状况下你应该使用Replace Array with Object 。
13. Replace Type Code with Class(以类取代型别码)
应用场景:在以C 为基础的编程语言中,type code(型别码)或枚举值(enumerations)很常见。若是带着一个有意义的符号名,type code 的可读性仍是不错的。问题在于,符号名终究只是个别名,编译器看见的、进行型别检验的,仍是背后那个数值。任何接受type code 做为引数(argument)的函数,所指望的其实是一个数值,没法强制使用符号名。这会大大下降代码的可读性,从而成为臭虫之源。若是把那样的数值换成一个class ,编译器就能够对这个class 进行型别检验。只要为这个class 提供factory methods ,你就能够始终保证只有合法的实体才会被建立出 来,并且它们都会被传递给正确的宿主对象。可是,在使用Replace Type Code with Class 以前,你应该先考虑type code 的其余替换方式。只有当type code 是纯粹数据时(也就是type code 不会在switch 语句中引发行为变化时),你才能以class 来取代它。Java 只能以整数做为switch 语句的「转辙」依据,不能使用任意class ,所以那种状况下不可以以class 替换type code 。更重要的是:任何switch 语句都应该运用 Replace Conditional with Polymorphism 去掉。为了进行那样的重构,你首先必须运用 Replace Type Code with Subclasses 或Replace Type Code with State/Strategy 把type code处理掉。即便一个type code 不会因其数值的不一样而引发行为上的差别,宿主类中的某些行为仍是有可能更适合置放于type code class 中,所以你还应该留意是否有必要使用Move Method 将一两个函数搬过去。
14. Replace Type Code with Subclasses(以子类取代型别码)
应用场景:若是你面对的type code 不会影响宿主类的行为,你可使用Replace Type Code with Class 来处理它们。但若是type code 会影响宿主类的行为,那么最好的办法就是借助多态(polymorphism )来处理变化行为。通常来讲,这种状况的标志就是像switch 这样的条件式。这种条件式可能有两种表现形式:switch 语句或者if-then-else 结构。不论哪一种形式,它们都是检查type code 值,并根据不一样的值执行不一样的动做。这种状况下你应该以Replace Conditional with Polymorphism 进行重构。但为了可以顺利进行那样的重构,首先应该将type code 替换为可拥有多态行为的继承体系。这样的一个继承体系应该以type code 的宿主类为base class,并针对每一种type code 各创建一个subclass 。为创建这样的继承体系,最简单的办法就是Replace Type Code with Subclasses:以type code 的宿主类为base class,针对每种type code 创建相应的subclass 。 可是如下两种状况你不能那么作:(1) type code 值在对象建立以后发生了改变;(2) 因为某些缘由,type code 宿主类已经有了subclass 。若是你刚好面临这两种状况之一,就须要使用Replace Type Code with State/Strategy 。Replace Type Code with Subclasses 的主要做用实际上是搭建一个舞台,让Replace Conditional with Polymorphism 得以一展身手。若是宿主类中并无出现条件式,那么 Replace Type Code with Class 更合适,风险也比较低。使用Replace Type Code with Subclasses 的另外一个缘由就是,宿主类中出现 了「只与具有特定type code 之对象相关」的特性。完成本项重构以后,你可使用 Push Down Method 和 Push Down Field 将这些特性推到合适的subclass去,以彰显它们「只与特定状况相关」这一事实。Replace Type Code with Subclasses 的好处在于:它把「对不一样行为的了解」从class 用户那儿转移到了class 自身。若是须要再加入新的行为变化,我只需添加subclass 一个就好了。若是没有多态机制,我就必须找到全部条件式,并逐一修改它们。所以,若是将来还有可能加入新行为,这项重构将特别有价值。
15. Replace Type Code with State/Strategy(以State/strategy 取代型别码)
应用场景:本项重构和Replace Type Code with Subclasses 很类似,但若是「type code 的值在对象生命期中发生变化」或「其余缘由使得宿主类不能被subclassing 」,你也可使用本重构。本重构使用State 模式或Stategy 模式[Gang of Four]。State 模式和Stategy 模式很是类似,所以不管你选择其中哪个,重构过程都是相同的。「选择哪个模式」并不是问题关键所在,你只须要选择更适合特定情境的模式就好了。若是你打算在完成本项重构以后再以 Replace Conditional with Polymorphism 简化一个算法,那么选择Stategy 模式比较合适;若是你打算搬移与状态相关(state-specific)的数据,并且你把新建对象视为一种变迁状态 (changing state),就应该选择使用State 模式。
16. Replace Subclass with Fields(以值域取代子类)
应用场景:创建subclass 的目的,是为了增如新特性,或变化其行为。有一种变化行为(variant behavior )称为「常量函数」(constant method)[Beck],它们会返回一个硬编码 (hard-coded)值。这东西有其用途:你可让不一样的subclasses 中的同一个访问函数(accessors)返回不一样的值。你能够在superclass 中将访问函数声明为抽象函数, 并在不一样的subclass 中让它返回不一样的值。尽管常量函数有其用途,但若subclass 中只有常量函数,实在没有足够的存在价值。 你能够在中设计一个与「常量函数返回值」相应的值域,从而彻底去除这样的subclass 。如此一来就能够避免因subclassing 而带来的额外复杂性。