代码重构(五):继承关系重构规则

陆陆续续的发表了多篇关于重构的文章了,还是那句话,重构是一个项目迭代开发中必不可少的一个阶段。其实重构伴随着你的项目的整个阶段。在前几篇关于重构的文章中我们谈到了函数的重构、类的重构、数据的重构以及条件表达式的重构,那么今天咱们就来聊聊继承关系的重构。当然还是延续前几篇博客的风格,我们在博客中的代码实例依然使用Swift语言来实现,当然还是那句话,使用什么语言无所谓,关键是看重构的场景以及重构的思想。

“重构”不仅仅可以改善你既有的代码设计,还可以改变你组织代码的思路,使你的程序在设计之初就趋于合理化,利于程序的扩充。重构往往伴随着设计模式的使用,在重构系列的博客结束后,我想系统的给大家分享一下关于设计模式的东西。当然是结合着各种实例。所谓一名Coder,重构和设计模式是必须涉猎的部分,因为这两者可以让你写出更漂亮的代码,当然要想真正的掌握设计模式以及各种重构手法,还得结合不同的实例来进行实践。理论固然重要,但是要想将理论的东西变成你自己的,还必须将理论付诸实践。废话少说,进入今天的主题。

一.Pull Up Field (字段上移) & Pull Down Field (字段下移)

字段上移与字段下移是相对的,也是我们之前所说的“凡事都有其两面性”,我们要辩证的去看待。我们只对Pull Up Field (字段上移) 这个规则做讨论,那么关于Pull Down Field (字段下移)我们不做过多的讨论,因为这两条规则是相反的,理解一条后,把这条规则反过来就是我们要理解的另一条规则。这样说起来,还是比“举一反三”要容易的多。

下方这个实例是为了解释“字段上移”所实现的一个Demo。当然Demo看上去不仅简单而且是有些夸张的,不过说明字段上移这个规则是完全足够了的。比如我们有一个父类为MySuperClass,我们有一个子类SubClass1,而在SubClass1中有一个字段父类是没有的。因为后期需求迭代或者需求变更,我们需要再创建一个SubClass1的兄弟类,就是下方的SubClass2。在SubClass2中与SubClass1中存在相同的字段,那就是var a = 0。

 在上述情况下,就需要使用到我们的“字段上移”的规则。也就是说将子类中相同的字段移到父类中。在该实例中就是讲var a = 0 移到父类中。重构后的代码如下所示:

而将“Pull Down Field (字段下移)”正好与上面的情况相反。也就是父类中有某些字段,但是这些字段只有在少数子类中使用到,在这种情况下我们需要将这个字段移到相应的子类中即可。除了Pull Up Field (字段上移) & Pull Down Field (字段下移) 这两个规则外,Pull Up Method (将函数上移) 和 Pull Down Method (将函数下移)这两个规则与上述情况类似。就是将上面的字段改成函数,有时候不仅字段会出现上述情况,函数也会出现上述情况,需要我们进行移动。因为使用场景类似,再次就不做过多的赘述了。

二、Extract Subclass (提炼子类)

这种情况下用的还是比较多的,当类中的某些方法只有在特定的类的实例中才会使用到,此时我们就需要提炼出一个子类,将该方法放到相应的子类中。这样一来我们的每个类的职责更为单一,这也就是我们常说的“单一职责”。

在下方示例中,CustomerBook是一个图书消费者的类。其中customeCharge()方法是普通用户计算消费金额所需的方法,而vipCharge()方法是VIP用户调用的方法,在内部vipCharge()需要调用customeCharege()方法。但是对外部而言,vipCharge()方法只有VIP用户才会用到,在这种情况下我们就需要使用“Extract Subclass (提炼子类)”规则对VIP进行提炼。

具体做法是我们需要提炼出一个子类,也就是说将VIP用户作为普通用户的子类,然后将只有VIP用户才调用的方法放到我们的VIP子类中。这样一来层次更加明确,每个类的职责更为单一。上述示例重构后的结果如下所示。

与“提炼子类”规则相对应的是“Collapse Hierarchy (折叠继承关系)”。一句话来概括:就是当你的父类与子类差别不大时,我们就可以将子类与父类进行合并。将上面的示例翻转就是“Collapse Hierarchy (折叠继承关系)”规则的示例,再次就不做过多的赘述了。

三、Form Template Method (构造模板函数)

Form Template Method (构造模板函数)这一规则还是比较实用的。先说模板,“模板”其实就是框架,没有具体的实现细节,只有固定不变的步骤,可以说模板不关心具体的细节。举个栗子?,像前段时间比较火的“秘密花园”,那些没有颜色的线条就是模板,如果一些人获取的是同一本秘密花园,那么说明每个人所获取的模板是相同的。但是每个人对每块的区域所图的颜色又有差异,这就是实现细节的不同。

言归正传,当两个兄弟类中的两个函数中的实现步骤大致一直,但是具体细节不同。在这种情况下,我们就可以将大体的步骤提取成模板,放到父类中,而具体细节由各自的子类来实现。具体实现请看下方的类,在Subclass1和Subclass2中的calculate()方法中的大体步骤是相同的,就是对两个值相加,然后返回这两个值的和。但是具体细节不同,可以看出两个相加值的具体计算方式不同。

在上述情况下我们就可以使用“Form Template Method (构造模板函数)”规则将相同的计算流程进行提取,也就是构造我们的模板函数。将模板函数放到两个类的父类中,然后在相应的子类中只给出实现细节即可。下方代码段是重构后的代码,父类中多出的方法就是我们提取的模板函数,而子类中只给出相应的实现细节即可。

四、以委托取代继承(Replace Inheritance with Delegation)

有时候我们为一些类创建子类后,发现子类只使用了父类的部分方法,而且没有继承或者部分继承了父类的数据。在这种情况下我们就可以将这种继承关系修改成委托的关系。具体做法就是修改这种继承关系,在原有子类中添加父类的对象字段,在子类中创建相应的方法,在方法中使用委托对象来调用原始父类中相应的方法。

下方示例是我们假想出来的,但是说明该规则是绰绰有余了。我们假设SubClass01类中只会用到SuperClass01中的display()方法,而没有继承父类中的数据。在下方示例中是继承关系,在这种情况下我们需要将其转换成委托关系。

下方是我们重构后的代码,在下方代码中我们去除了之前的继承关系。并在子类中创建了一个之前父类的代理对象,并且创建了一个相应的方法,在该新建的方法中通过代理对象来调用相应的方法。具体如下所示。

上述规则与以继承取代委托(Replace Delegation with Inheritance)原则相对于,使用情况与上述相反,再次就不做过多的赘述了。

几天博客就先到这儿,内容比较简单,但是还是比较重要的。

上述Demo分享地址:https://github.com/lizelu/CodeRefactoring-Swift

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏海纳周报

【第四期】GC专题

我在某个技术群里发现很多人对GC的问题是最多的。确实,由于Java的GC经常会刷存在感(例如占用大量的CPU时间,full gc时直接失去响应),GC的问题就成...

37790
来自专栏韩伟的专栏

基于对象和面向对象

“基于对象”是“面向对象”一次动态化变迁,它依赖于现代语言的动态特性,让方法和属性统一起来;用组合取代继承;以函数对象查找取代多态的方法调用。这些变化让面向对象...

1.5K00
来自专栏带你撸出一手好代码

JavaScript消除游戏实现思路讲解

之前讲解过一款JavaScript贪食蛇游戏详细的设计与实现,但是以那种方式进行描述 , 整篇文章会显得复杂冗长,除非深入细致的阅读和思考,否则文中内容并不容易...

40150
来自专栏后端沉思录

回调函数

什么是回调函数,上面的问题说的是不是很空洞,不是太形象,下面是知乎上的一位网友给的答案:

42120
来自专栏杨建荣的学习笔记

Python代码重构参考

在数据库方向上相对来说能够容易推出开发规范和标准,但是你很少听到公司里面出针对开发同学的开发规范。我觉得其中的一个原因是程序猿比较执拗,以技术服人,经常...

29000
来自专栏抠抠空间

python常见模块之time模块

一、模块简介 在Python中,通常有这几种方式来表示时间: 时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按...

34670
来自专栏带你撸出一手好代码

kotlin语言使用初体验(一)

居说谷歌新认的干儿子kotlin极为受宠,隐隐有替代Java在 android平台老大位置的趋势。kotlin有谷歌撑腰,加上自己的底子也厚,再之与Java无缝...

35250
来自专栏Golang语言社区

谁发明了Go?

谁发明了Go Go语言的设计和实现工作是由Google的一个研发小组以及来自世界各地的大量贡献者共同完成的。 动机 为什么要发明一门新语言? 在当今世界,编程语...

70640
来自专栏Web行业观察

从JSON进化到BSON

自从MEAN引导的JSON数据格式取代传统JAVA推崇的XML以后, json的发展却停滞不前了, 当然这是好事, 因为稳定的结构是不需要向下兼...

41740
来自专栏Crossin的编程教室

【Python 第51课】 and-or技巧

今天介绍一个python中的小技巧:and-or 看下面这段代码: a = "heaven" b = "hell" c = True and a or b p...

29890

扫码关注云+社区

领取腾讯云代金券