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

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

相关文章

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

MySQL数据库的内嵌函数和联合查询实例代码

《MySQL数据库的内嵌函数和联合查询实例代码》联合查询是一种将多个查询结果组合在一起的方法,通常使用UNION、UNIONALL、INTERSECT和EXCEPT关键字,下面:本文主要介绍MyS... 目录一.数据库的内嵌函数1.1聚合函数COUNT([DISTINCT] expr)SUM([DISTIN

Java实现自定义table宽高的示例代码

《Java实现自定义table宽高的示例代码》在桌面应用、管理系统乃至报表工具中,表格(JTable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,而JavaSwing... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)

HTML5实现的移动端购物车自动结算功能示例代码

《HTML5实现的移动端购物车自动结算功能示例代码》本文介绍HTML5实现移动端购物车自动结算,通过WebStorage、事件监听、DOM操作等技术,确保实时更新与数据同步,优化性能及无障碍性,提升用... 目录1. 移动端购物车自动结算概述2. 数据存储与状态保存机制2.1 浏览器端的数据存储方式2.1.

基于 HTML5 Canvas 实现图片旋转与下载功能(完整代码展示)

《基于HTML5Canvas实现图片旋转与下载功能(完整代码展示)》本文将深入剖析一段基于HTML5Canvas的代码,该代码实现了图片的旋转(90度和180度)以及旋转后图片的下载... 目录一、引言二、html 结构分析三、css 样式分析四、JavaScript 功能实现一、引言在 Web 开发中,

Python如何去除图片干扰代码示例

《Python如何去除图片干扰代码示例》图片降噪是一个广泛应用于图像处理的技术,可以提高图像质量和相关应用的效果,:本文主要介绍Python如何去除图片干扰的相关资料,文中通过代码介绍的非常详细,... 目录一、噪声去除1. 高斯噪声(像素值正态分布扰动)2. 椒盐噪声(随机黑白像素点)3. 复杂噪声(如伪