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

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

相关文章

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

Python如何计算两个不同类型列表的相似度

《Python如何计算两个不同类型列表的相似度》在编程中,经常需要比较两个列表的相似度,尤其是当这两个列表包含不同类型的元素时,下面小编就来讲讲如何使用Python计算两个不同类型列表的相似度吧... 目录摘要引言数字类型相似度欧几里得距离曼哈顿距离字符串类型相似度Levenshtein距离Jaccard相

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

Redis存储的列表分页和检索的实现方法

《Redis存储的列表分页和检索的实现方法》在Redis中,列表(List)是一种有序的数据结构,通常用于存储一系列元素,由于列表是有序的,可以通过索引来访问元素,因此可以很方便地实现分页和检索功能,... 目录一、Redis 列表的基本操作二、分页实现三、检索实现3.1 方法 1:客户端过滤3.2 方法