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

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通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

Tomcat版本与Java版本的关系及说明

《Tomcat版本与Java版本的关系及说明》:本文主要介绍Tomcat版本与Java版本的关系及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Tomcat版本与Java版本的关系Tomcat历史版本对应的Java版本Tomcat支持哪些版本的pythonJ

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN