本文主要是介绍《重构改善既有代码的设计》之重构列表--处理概括关系(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
五、Push Down Field(下移字段)
超类中的某个字段只被部分(而非全部)子类用到。
将这个字段移到需要它的子类中去。
动机
Push Down Field 与Pull Up Field恰恰相反:如果只有某些(而非全部)子类需要超类内的一个字段,你可以使用本项重构。
做法
1、在所有子类中声明该字段。
2、将该字段从超类中移除。
3、编译,测试。
4、将该字段从所有不需要它的那些子类中删掉。
5、编译,测试。
六、Extract Subclass (提炼子类)
类中的某些特性只被某些(而非全部)实例用到。
新建一个子类,将上面所说的那一部分特性移到子类中。
动机
使用Extract Subclass的主要动机是:你发现类中的某些行为只被一部分实例用到,其他实例不需要它们。有时候这种行为上的差异是通过类型码区分的,此时你可以使用Replace Type Code with Subclass或Replace Type Code with State/Strategy。但是,并非一定要出现了类型码才表示需要考虑使用子类。
Extract Class是Extract Subclass之外的另一种选择,两者之间的抉择其实就是委托和继承之间的抉择。Extract Subclass 通常更容易进行,但它也有限制:一旦对象创建完成,你无法再改变与类型相关的行为。但如果使用Extract Class,你只需插入另一个组件就可以改变对象行为。此外,子类只能用以表现一组变化。如果你需要一个类以几种不同的方式变化,就必须使用委托。
做法
1、为源类定义一个新类。
2、为这个新类的子类提供构造函数。
? 简单的做法是:让子类构造函数接受与超类构造函数相同的参数,并通过super调用超类构造函数。
? 如果你希望对用户隐藏子类的存在,可使用Replace Constructor with Factory Method。
3、找出调用超类构造函数的所有地点。如果它们需要的是新建的子类,令它们改而调用新构造函数。
? 如果子类构造函数需要的参数和超类构造函数不同,可以使用Rename Method 修改其参数列。如果子类构造函数不需要超类构造函数的某些参数,可以使用Rename Method将它们去除。
? 如果不再需要直接创建超类实例,就将超类声明为抽象类。
4、逐一使用Push Down Method和Push Down Field将源类的特性移到子类去。
? 和Extract Class不同的是:先处理函数再处理数据,通常会简单一些。
? 当一个public函数被下移到子类后,你可能需要重新定义该函数的调用端的局部变量或参数类型,让它们改而去调用子类中的新函数。如果忘记进行这一步骤,编译器会提醒你。
5、找到所有这样的字段:它们所传达的信息如今可由继承提醒自身传达(这一类字段通常是boolean变量或类型码)。以Self Encapsulate Field避免直接使用这些字段,然后将它们的取值函数替换为多态常量函数。所有使用这些字段的地方都 应该以Replace Conditional with Polymorphism重构。
? 任何函数如果位于源类之外,而又使用了上述字段的访问函数,考虑Move Method将它移到源类中,然后再使用Replace Conditional with Polymorphism。
6、每次下移之后,编译并测试。
七、Extract Superclass(提炼超类)
两个类有相似特性。
为这两个类建立一个超类,将相同特性移至超类。
动机
重复代码是系统中最糟糕的东西之一。如果你在不同地方做同一件事情,一旦需要修改那些动作,你就得平白做更多修改。
重复代码的某种形式就是:两个类以相同的方式做类似的事情,或者以不同的方式做类型的事情。对象提供了一种简化这种情况的机制,那就是继承。但是,在建立这些具有共通性的类之前,你往往无法发现这样的共通性,因此经常会在具有共通性的类出现之后,再开始建立其间的继承结构。
另一种选择就是Extract Class。这两种方案之间的选择其实就是继承和委托之间的选择。如果两个类可以共享行为,也可以共享接口,那么继承是比较简单的做法。如果你选错了,也总有Replace Inheritance with Delegation这瓶后悔药可吃。
做法
1、为原本的类新建一个空白的抽象超类。
2、运用Pull Up Field 、Pull Up Method和Pull Up Constructor Body 逐一将子类的共同元素上移到超类。
? 先搬移字段,通常比较简单。
? 如果相应的子类有不同的签名,但用途相同,可以先使用Rename Method将它们签名改为相同,然后再使用Pull Up Method。
? 如果相应的子类函数又相同的签名,但函数本体不同,可以在超类中把它们的共同签名声明为抽象函数。
? 如果相应的子类有不同的函数本体,但用途相同,可以试着使用 Substitute Algorithm 把其中一个函数的函数本体复制到另一个函数中。如果运转正常,你就可以使用Pull Up Method。
3、每次上移后,编译并测试。
4、检查留在子类中的函数,看它们是否还有共通部分。如果有,可以使用Extract Method将共通部分再提炼出来,然后使用Pull Up Method将提炼出来的函数上移到超类。如果各个子类中某个函数的整体流程很相似,你也许可以使用Form Template Method。
5、将所有共通元素都上移到超类之后,检查子类的所有用户。如果它们只使用共同接口,你就可以把它们请求的对象类型改为超类。
八、Extract Interface (提炼接口)
若干用户使用类接口中的同一子集,或者两个类的接口有部分相同。
将相同的子集提炼到一个独立的接口中。
动机
类之间批次互用的方式有若干种。“使用一个类”通常意味用到该类的所有责任区。另一种情况是,某一组客户只使用类责任区中一个特定子集。再一种情况则是,这个类需要与所有协助处理某些特定请求的类合作。
对于后两种情况,将真正用到的这部分责任分离出来通常很有意义,因为这样可以使系统的用法更清晰,同时也更容易看清系统的责任划分。如果新的类需要支持上述子集,也比较能够看清子集内有些什么东西。
在许多面向对象语言中,这种责任时通过多继承来实现的。你可以针对每组行为建立一个类,再将它们组合于同一实现中。Java提供单继承,但你可以运用接口来昭示并实现上述需求。接口对于java程序的设计方式有着巨大影响,就连Smalltalk程序员都认为接口是一大进步!
Extract Superclass 和Extract Interface之间有些相似之处。Extract Interface 只能提炼共通接口,不能提炼共通代码。使用Extract Interface 可能造成难闻的“重复”坏味道,幸而你可以运用Extract Class 先把共通行为放进一个组件中,然后将工作委托该组件,从而解决这个问题。如果有不少共通行为,Extract Superclass会比较简单,但是每个类只能有一个超类。
如果某个类在不同环境下扮演截然不同的角色,使用接口就是一个好主意。你可以针对每个角色以Extract Interface提炼出相应接口。另一种可以用上Extract Interface的情况是:你想要描述的一个类的外部依赖接口(outbound interface,即这个类要求服务方提供的操作)。如果你打算将来加入其它种类的访问对象,只需要他们实现这个接口即可。
做法
1、新建一个空接口。
2、在接口中声明待提炼类的共通动作。
3、让相关的类实现上述接口。
4、调整客户端的类型声明,令其使用该接口。、
这篇关于《重构改善既有代码的设计》之重构列表--处理概括关系(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!