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

2023-12-16 14:48

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

五、Push Down Field(下移字段)

超类中的某个字段只被部分(而非全部)子类用到。

将这个字段移到需要它的子类中去。

动机

Push Down Field Pull Up Field恰恰相反:如果只有某些(而非全部)子类需要超类内的一个字段,你可以使用本项重构。

做法

1、在所有子类中声明该字段。

2、将该字段从超类中移除。

3、编译,测试。

4、将该字段从所有不需要它的那些子类中删掉。

5、编译,测试。

六、Extract Subclass (提炼子类)

类中的某些特性只被某些(而非全部)实例用到。

新建一个子类,将上面所说的那一部分特性移到子类中。

动机

使用Extract Subclass的主要动机是:你发现类中的某些行为只被一部分实例用到,其他实例不需要它们。有时候这种行为上的差异是通过类型码区分的,此时你可以使用Replace Type Code with SubclassReplace Type Code with State/Strategy。但是,并非一定要出现了类型码才表示需要考虑使用子类。

Extract ClassExtract Subclass之外的另一种选择,两者之间的抉择其实就是委托和继承之间的抉择。Extract Subclass 通常更容易进行,但它也有限制:一旦对象创建完成,你无法再改变与类型相关的行为。但如果使用Extract Class,你只需插入另一个组件就可以改变对象行为。此外,子类只能用以表现一组变化。如果你需要一个类以几种不同的方式变化,就必须使用委托。

做法

1、为源类定义一个新类。

2、为这个新类的子类提供构造函数。

简单的做法是:让子类构造函数接受与超类构造函数相同的参数,并通过super调用超类构造函数。

如果你希望对用户隐藏子类的存在,可使用Replace Constructor with Factory Method

3、找出调用超类构造函数的所有地点。如果它们需要的是新建的子类,令它们改而调用新构造函数。

如果子类构造函数需要的参数和超类构造函数不同,可以使用Rename Method 修改其参数列。如果子类构造函数不需要超类构造函数的某些参数,可以使用Rename Method将它们去除。

如果不再需要直接创建超类实例,就将超类声明为抽象类。

4、逐一使用Push Down MethodPush Down Field将源类的特性移到子类去。

Extract Class不同的是:先处理函数再处理数据,通常会简单一些。

当一个public函数被下移到子类后,你可能需要重新定义该函数的调用端的局部变量或参数类型,让它们改而去调用子类中的新函数。如果忘记进行这一步骤,编译器会提醒你。

5、找到所有这样的字段:它们所传达的信息如今可由继承提醒自身传达(这一类字段通常是boolean变量或类型码)。以Self Encapsulate Field避免直接使用这些字段,然后将它们的取值函数替换为多态常量函数。所有使用这些字段的地方都 应该以Replace Conditional with Polymorphism重构。

任何函数如果位于源类之外,而又使用了上述字段的访问函数,考虑Move Method将它移到源类中,然后再使用Replace Conditional with Polymorphism

6、每次下移之后,编译并测试。

七、Extract Superclass(提炼超类)

两个类有相似特性。

为这两个类建立一个超类,将相同特性移至超类。

动机

重复代码是系统中最糟糕的东西之一。如果你在不同地方做同一件事情,一旦需要修改那些动作,你就得平白做更多修改。

重复代码的某种形式就是:两个类以相同的方式做类似的事情,或者以不同的方式做类型的事情。对象提供了一种简化这种情况的机制,那就是继承。但是,在建立这些具有共通性的类之前,你往往无法发现这样的共通性,因此经常会在具有共通性的类出现之后,再开始建立其间的继承结构。

另一种选择就是Extract Class。这两种方案之间的选择其实就是继承和委托之间的选择。如果两个类可以共享行为,也可以共享接口,那么继承是比较简单的做法。如果你选错了,也总有Replace Inheritance with Delegation这瓶后悔药可吃。

做法

1、为原本的类新建一个空白的抽象超类。

2、运用Pull Up Field Pull Up MethodPull Up Constructor Body 逐一将子类的共同元素上移到超类。

先搬移字段,通常比较简单。

如果相应的子类有不同的签名,但用途相同,可以先使用Rename Method将它们签名改为相同,然后再使用Pull Up Method

如果相应的子类函数又相同的签名,但函数本体不同,可以在超类中把它们的共同签名声明为抽象函数。

如果相应的子类有不同的函数本体,但用途相同,可以试着使用 Substitute Algorithm 把其中一个函数的函数本体复制到另一个函数中。如果运转正常,你就可以使用Pull Up Method

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

4、检查留在子类中的函数,看它们是否还有共通部分。如果有,可以使用Extract Method将共通部分再提炼出来,然后使用Pull Up Method将提炼出来的函数上移到超类。如果各个子类中某个函数的整体流程很相似,你也许可以使用Form Template Method

5、将所有共通元素都上移到超类之后,检查子类的所有用户。如果它们只使用共同接口,你就可以把它们请求的对象类型改为超类。

八、Extract Interface (提炼接口)

若干用户使用类接口中的同一子集,或者两个类的接口有部分相同。

将相同的子集提炼到一个独立的接口中。

动机

类之间批次互用的方式有若干种。“使用一个类”通常意味用到该类的所有责任区。另一种情况是,某一组客户只使用类责任区中一个特定子集。再一种情况则是,这个类需要与所有协助处理某些特定请求的类合作。

对于后两种情况,将真正用到的这部分责任分离出来通常很有意义,因为这样可以使系统的用法更清晰,同时也更容易看清系统的责任划分。如果新的类需要支持上述子集,也比较能够看清子集内有些什么东西。

在许多面向对象语言中,这种责任时通过多继承来实现的。你可以针对每组行为建立一个类,再将它们组合于同一实现中。Java提供单继承,但你可以运用接口来昭示并实现上述需求。接口对于java程序的设计方式有着巨大影响,就连Smalltalk程序员都认为接口是一大进步!

Extract Superclass Extract Interface之间有些相似之处。Extract Interface 只能提炼共通接口,不能提炼共通代码。使用Extract Interface 可能造成难闻的“重复”坏味道,幸而你可以运用Extract Class 先把共通行为放进一个组件中,然后将工作委托该组件,从而解决这个问题。如果有不少共通行为,Extract Superclass会比较简单,但是每个类只能有一个超类。

如果某个类在不同环境下扮演截然不同的角色,使用接口就是一个好主意。你可以针对每个角色以Extract Interface提炼出相应接口。另一种可以用上Extract Interface的情况是:你想要描述的一个类的外部依赖接口(outbound interface,即这个类要求服务方提供的操作)。如果你打算将来加入其它种类的访问对象,只需要他们实现这个接口即可。

做法

1、新建一个空接口。

2、在接口中声明待提炼类的共通动作。

3、让相关的类实现上述接口。

4、调整客户端的类型声明,令其使用该接口。、

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



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

相关文章

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 方法