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

2023-12-16 14:48

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

九、Collapse Hierarchy(折叠继承体系)

超类和子类之间无太大区别。

将它们合为一体。

动机

如果你曾经编写过继承体系,就会知道,继承体系很容易变得过分复杂。所谓重构继承体系,往往是将函数和字段在体系中上下移到。完成这些动作后,你狠可能发现某个子类并未带来该有的价值,因此需要把超类和子类合并起来。

做法

1、选择你想移除的类:是超类还是子类?

2、使用Pull Up Field 和 Pull Up Method,或者Push Down FieldPush Down Method,把想要移除的类的所有行为和数据搬移到另一个类。

3、每次移动后,编译并测试。

4、调整即将被移除的那个类的所有引用点,令它们改而引用合并后留下的那个类。

。这个动作将会影响变量的声明、参数的类型以及构造函数。

5、移除我们的目标:此时的它应该已经成为一个空类。

6、编译,测试。

十、Form Template Method(塑造模板函数)

你有一些类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上有所不同。

将这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了。然后将原函数上移至超类。

动机

继承是避免重复行为的一个强大工具。无论何时,只要你看见两个子类中有类似的函数,就可以把它们提升到超类。但是如果这些函数并不完全相同该怎么办?我们仍有必要尽量避免重复,但又必须保持这些函数之间的实质差异。

常见的一种情况是:两个函数以相同顺序执行大致相近的操作,但是各个操作不完全相同。这种情况下我们可以将执行操作的序列移至超类,并借助多态保证各操作仍得以保持差异性。这样的函被称为Template Method(模板函数)

做法

1、在各个子类中分解目标函数,使分解后的各个函数要不完全相同,要不完全不同。

2、运用Pull Up Method将各子类内完全相同的函数上移至超类。

3、对于那些(剩余的、存在于各个子类内的)完全不同的函数,实施Rename Method,使所有的这些函数的签名完全不同。

这将使得原函数变为完全相同,因为它们都执行统一一组函数调用;但各子类会以不同方式相应这些调用。

4、修改上述所有签名后,编译并测试。

5、运用Pull Up Method将所有原函数逐一上移至超类。在超类中将那些代表各个不同操作的函数定义为抽象函数。

6、编译,测试。

7、移除其他子类中的原函数,每删除一个,编译并测试。

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

某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。

在子类中新建字段用以保存超类;调整子类函数,令它改而委托超类;然后去掉两者之间的继承关系。

动机

继承是个好东西,但有时候它并不是你要的。你常常会遇到这样的情况:一开始继承了一个类,随后发现超类中的许多操作并不真正适用于子类。这种情况下,你所拥有的接口并未真正反映出子类的功能。或者,你可能发现你从超类中继承了一大堆子类并不需要的数据,抑或你发现超类中的某些protected函数对子类并没有什么意义。

你可以选择容忍,并接受传统说法:子类可以只使用超类功能的一部分。但这样做的结果是:代码传达的信息与你的意图南辕北辙---这是一种混淆,你应该将它去除。

如果以委托取代继承,你可以更清楚地表明:你只需要受托类的一部分功能。接口中的那一部分应该被使用,哪一部分应该被忽略,完全由你主导控制。这样做的成本则是需要额外写出委托函数,但这些函数都非常简单,极少可能出错。

做法

1、在子类中新建一个字段,使其引用超类的一个实例,并将它初始化为this.

2、修改子类内的所有函数,让它们不再使用超类,转而使用上述那个受托字段。每次修改后,编译并测试。

你不能这样修改子类中通过super调用的超类函数的代码,否则它们会陷入无限递归。这种函数只有在继承体系被打破后才能修改。

3、去除两个类之间的继承关系,新建一个受托类的对象赋给受托字段。

4、针对客户端所用的每一个超类函数,为它添加一个简单的受托函数。

5、编译,测试。

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

你在两个类之间使用委托关系,并经常为整个接口编写许多简单的委托函数。

让委托类继承受托类。

动机

本重构与Replace Inheritance with Delegation恰恰相反。如果你发现自己需要使用受托类中的所有函数,并且费了很大力气编写所有极简的委托函数,本重构可以帮助你轻松回头使用继承。

两条告诫需牢记于心。首先,如果你并没有使用受托类的所有函数,那么就不应该使用Replace Delegation with Inheritance,因为子类应该总是遵循超类的接口。如果过多的委托函数让你烦心,你有别的选择:你可以通过Remove Middle Man容乃公客户端自己调用受托函数,也可以使用Extract Superclass将两个类接口相同的部分提炼到超类中,然后让两个类都继承这个新的超类;你还可以用类似的手法使用Extract Interface

另一种需要当心的情况是:受托对象被不止一个其他对象共享,而且受托对象是可变的。在这种情况下,你就不能将委托关系替换为继承关系,因为这样就无法再共享数据了。数据共享是必须由委托关系承担的一种责任,你无法把它转给继承关系。如果受托对象是可变的,数据共享就不成问题,因为你大可放心的复制对象,谁都不会知道。

做法

1、让委托端成为受托端的一个子类。

2、编译。

此时,某些函数可能会发生冲突:它们可能有相同的名称,但在返回类型、异常指定或可见程度方面有所差异。你可以使用Rename Method解决此类问题。、

3、将受托字段设为该字段所处对象本身。

4、去掉简单的委托函数。

5、编译并测试。

6、将所有其他涉及委托关系的代码,改为调用对象自身。

7、移除受托字段。

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



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

相关文章

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Python使用vllm处理多模态数据的预处理技巧

《Python使用vllm处理多模态数据的预处理技巧》本文深入探讨了在Python环境下使用vLLM处理多模态数据的预处理技巧,我们将从基础概念出发,详细讲解文本、图像、音频等多模态数据的预处理方法,... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

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.