《重构改善既有代码的设计》之重构列表--处理概括关系(一)

2023-12-16 14:48

本文主要是介绍《重构改善既有代码的设计》之重构列表--处理概括关系(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

有一批重构手法专门用来处理概括关系(既继承关系)。

一、Pull Up Field(字段上移)

两个子类拥有相同的字段。

将该字段移至超类中。

动机

如果各子类是分别开发的,或者是在重构过程中组合起来的,你常会发现他们拥有重复特性,特别是字段更容易重复,这样的字段有时拥有相似的名字,但也并非绝对如此。判断若干字段是否重复,唯一的办法是观察函数如果使用它们。如果它们被使用的方式相似,你就可以将它们归纳到超类中。

本项重构从两方面减少重复:首先它去除了重复的数据声明;其次它使你可以将使用该字段的行为从子类一直超类,从而去除重复行为。

做法

1、针对待提升之字段,检查它们的所有被引用点,确认它们以同样的方式被使用。

2、如果这些字段的名称不同,先将它们改名,使每一个名称都和你想为超类字段取的名称相同。

3、编译并测试。

4、在超类中新建一个字段。

5、移除子类中的字段。

6、编译,测试。

7、考虑对超类的新建字段使用Self Encapsulate Field

二、Pull Up Method(函数上移)

有些函数,在各个子类中产品完全相同的结果。

将该函数移至超类。

动机

避免行为重复是很重要的。尽管重复的两个函数也可以很好的工作,但重复自身只会成为错误的滋生地,此外别无价值。无论何时,只有系统之内出现重复,你就会面临“修改其中一个却未能修改另一个”的风险。通常,找出重复也有一定的困难。

如果某个函数在各子类中的函数体都相同(它们很可能是通过复制粘贴得到的),这就是最显而易见的Pull Up Method适用场合。当然,情况并不总是如此明显。你也可以只管放心重构,再看看测试程序会不会发牢骚,但这就需要对你的测试有充分的信心。我发现观察这些可能重复的函数之间的差异往往大有收获:它们经常会向我们展示那些我忘记测试的行为。

Pull Up Method常常紧随其他重构而被使用。也许你能找出若干个身处不同子类内的函数,而它们又可以通过某种形式的参数调整成为相同的函数。这时候,最简单的办法就是首先分别调整这些函数的参数,然后再将它们概括到超类中。当然,如果你足够自信,也可以一次完成这两个步骤。

有一种特殊情况也需要使用Pull Up  Method:子类的函数覆写了超类的函数,但却仍然做相同的工作。

Pull Up Method过程中最麻烦的一点是:被提升的函数可能会引用只出现于子类而不出现于超类的特性。如果被引用的是个函数,你可以将该函数也一同提升到超类中,或者在超类中建立一个抽象函数。在此过程中,你可能需要修改某个函数的签名,或建立一个委托函数。

如果两个函数相似但不相同,你或许可以先借助Form Template Method构造出相同的函数,然后再提升它们。

做法

1、检查待提升函数,确定它们是完全一致的。

如果这些函数看上去做了相同的事,但并非完全一致,可使用Substitute Algorithm 让它们变得完全一致。

2、如果待提升函数的签名不同,将那些签名都修改为你想要在超类中使用的签名。

3、在超类中新建一个函数,将某一个待提升的函数的代码复制到其中,做适当的调整,然后编译。

如果你使用的是一种强类型语言,而待提升函数又调用了一个只出现于子类而未出现于超类的函数,你可以在超类中为被调用函数声明一个抽象函数。

如果待提升函数使用了子类的一个字段,你可以使用Pull Up Field将该字段提升到超类中;或者也可以先使用Self Encapsulate Field,然后在超类中把取值函数声明为抽象函数。

4、移除一个待提升子类函数。

5、编译、测试。

6、逐一移除待提升的子类函数,直到只剩下超类中的函数为止。每次移除后都需要测试。

7、观察该函数的调用者,看看是否可以改为使用超类类型的对象。

三、Pull Up Constructor Body(构造函数本体上移)

你在各个子类中拥有一些构造函数,它们的本体几乎完全一致。

在超类中新建一个构造函数,并在子类构造函数中调用它。

动机

构造函数是很奇妙的东西。它们不会普通的函数,使用它们比使用普通函数受到更多的限制。

如果你看到各个子类的函数有共同的行为,第一个念头就是将共同行为提炼到一个独立函数中,然后将这个函数提升到超类。对构造函数而言,它们彼此的共同行为往往就是“对象的建构”。这时候你需要在超类中提供一个构造函数,然后让子类都来调用它。很多时候,子类构造函数的唯一动作就是调用超类构造函数。这里不能运用Pull Up Method ,因为你无法在子类中继承超类构造函数。

如果重构过程过于复杂,你可以先考虑使用Replace Constructor with Factory Method

做法

1、在超类中定义一个构造函数。

2、将子类构造函数中的共同代码搬移到超类构造函数中。

被搬移的可能是子类构造函数的全部内容。

首先设法将共同代码搬移到子类构造函数中,然后再复制到超类构造函数中。

3、将子类构造函数中的共同代码删掉,改而调用新建的超类构造函数。

如果子类构造函数中的所有代码都是一样的,那么子类构造函数就只需要调用超类构造函数。

4、编译、测试。

如果日后子类构造函数再出现相同代码,你可以首先使用Extract Method将那部分提炼到一个独立函数中,然后使用Pull Up Method将该函数上移到超类。

四、Push Down Method(函数下移)

超类中的某个函数只与部分(而非全部)子类有关。

将这个函数移到相关的子类去。

动机

Push Down Method 与 Pull Up Method恰恰相反。当我有必要把某些行为从超类移至特定的子类时,我就使用 Push Down Method,它通常也只有在这种时候使用。使用Extract Subclass之后你可能会需要它。

做法

1、在所有子类中声明该函数,将超类中的函数本体复制到每一个子类函数中。

你可能需要将超类的某些字段声明为protected,让子类函数也能够访问他们。如果日后你也想把这些字段下移到子类,通常就可以那么做;否则应该使用超类提供的访问函数。如果访问函数并非public,你得将它声明为protected

2、删除超类中的函数

你可能必须修改调用端的某些变量声明或参数声明,以便能够使用子类。

如果有必要通过一个超类对象访问该函数,或你不想把该函数从任何子类中移除,再或超类是抽象类,那么你就可以在超类中把该函数声明为抽象函数。

3、编译、测试。

4、将该函数从所有不需要它的那些子类中删除。

5、编译、测试。

这篇关于《重构改善既有代码的设计》之重构列表--处理概括关系(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/500854

相关文章

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

JAVA项目swing转javafx语法规则以及示例代码

《JAVA项目swing转javafx语法规则以及示例代码》:本文主要介绍JAVA项目swing转javafx语法规则以及示例代码的相关资料,文中详细讲解了主类继承、窗口创建、布局管理、控件替换、... 目录最常用的“一行换一行”速查表(直接全局替换)实际转换示例(JFramejs → JavaFX)迁移建

Go异常处理、泛型和文件操作实例代码

《Go异常处理、泛型和文件操作实例代码》Go语言的异常处理机制与传统的面向对象语言(如Java、C#)所使用的try-catch结构有所不同,它采用了自己独特的设计理念和方法,:本文主要介绍Go异... 目录一:异常处理常见的异常处理向上抛中断程序恢复程序二:泛型泛型函数泛型结构体泛型切片泛型 map三:文

Springboot3统一返回类设计全过程(从问题到实现)

《Springboot3统一返回类设计全过程(从问题到实现)》文章介绍了如何在SpringBoot3中设计一个统一返回类,以实现前后端接口返回格式的一致性,该类包含状态码、描述信息、业务数据和时间戳,... 目录Spring Boot 3 统一返回类设计:从问题到实现一、核心需求:统一返回类要解决什么问题?

MyBatis中的两种参数传递类型详解(示例代码)

《MyBatis中的两种参数传递类型详解(示例代码)》文章介绍了MyBatis中传递多个参数的两种方式,使用Map和使用@Param注解或封装POJO,Map方式适用于动态、不固定的参数,但可读性和安... 目录✅ android方式一:使用Map<String, Object>✅ 方式二:使用@Param

SpringBoot实现图形验证码的示例代码

《SpringBoot实现图形验证码的示例代码》验证码的实现方式有很多,可以由前端实现,也可以由后端进行实现,也有很多的插件和工具包可以使用,在这里,我们使用Hutool提供的小工具实现,本文介绍Sp... 目录项目创建前端代码实现约定前后端交互接口需求分析接口定义Hutool工具实现服务器端代码引入依赖获

Java中自旋锁与CAS机制的深层关系与区别

《Java中自旋锁与CAS机制的深层关系与区别》CAS算法即比较并替换,是一种实现并发编程时常用到的算法,Java并发包中的很多类都使用了CAS算法,:本文主要介绍Java中自旋锁与CAS机制深层... 目录1. 引言2. 比较并交换 (Compare-and-Swap, CAS) 核心原理2.1 CAS

利用Python在万圣节实现比心弹窗告白代码

《利用Python在万圣节实现比心弹窗告白代码》:本文主要介绍关于利用Python在万圣节实现比心弹窗告白代码的相关资料,每个弹窗会显示一条温馨提示,程序通过参数方程绘制爱心形状,并使用多线程技术... 目录前言效果预览要点1. 爱心曲线方程2. 显示温馨弹窗函数(详细拆解)2.1 函数定义和延迟机制2.2

SpringSecurity中的跨域问题处理方案

《SpringSecurity中的跨域问题处理方案》本文介绍了跨域资源共享(CORS)技术在JavaEE开发中的应用,详细讲解了CORS的工作原理,包括简单请求和非简单请求的处理方式,本文结合实例代码... 目录1.什么是CORS2.简单请求3.非简单请求4.Spring跨域解决方案4.1.@CrossOr

requests处理token鉴权接口和jsonpath使用方式

《requests处理token鉴权接口和jsonpath使用方式》文章介绍了如何使用requests库进行token鉴权接口的处理,包括登录提取token并保存,还详述了如何使用jsonpath表达... 目录requests处理token鉴权接口和jsonpath使用json数据提取工具总结reques