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

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

相关文章

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Python进行JSON和Excel文件转换处理指南

《Python进行JSON和Excel文件转换处理指南》在数据交换与系统集成中,JSON与Excel是两种极为常见的数据格式,本文将介绍如何使用Python实现将JSON转换为格式化的Excel文件,... 目录将 jsON 导入为格式化 Excel将 Excel 导出为结构化 JSON处理嵌套 JSON:

python中列表应用和扩展性实用详解

《python中列表应用和扩展性实用详解》文章介绍了Python列表的核心特性:有序数据集合,用[]定义,元素类型可不同,支持迭代、循环、切片,可执行增删改查、排序、推导式及嵌套操作,是常用的数据处理... 目录1、列表定义2、格式3、列表是可迭代对象4、列表的常见操作总结1、列表定义是处理一组有序项目的

Mysql中设计数据表的过程解析

《Mysql中设计数据表的过程解析》数据库约束通过NOTNULL、UNIQUE、DEFAULT、主键和外键等规则保障数据完整性,自动校验数据,减少人工错误,提升数据一致性和业务逻辑严谨性,本文介绍My... 目录1.引言2.NOT NULL——制定某列不可以存储NULL值2.UNIQUE——保证某一列的每一

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

Spring Boot 中的默认异常处理机制及执行流程

《SpringBoot中的默认异常处理机制及执行流程》SpringBoot内置BasicErrorController,自动处理异常并生成HTML/JSON响应,支持自定义错误路径、配置及扩展,如... 目录Spring Boot 异常处理机制详解默认错误页面功能自动异常转换机制错误属性配置选项默认错误处理

SpringBoot 异常处理/自定义格式校验的问题实例详解

《SpringBoot异常处理/自定义格式校验的问题实例详解》文章探讨SpringBoot中自定义注解校验问题,区分参数级与类级约束触发的异常类型,建议通过@RestControllerAdvice... 目录1. 问题简要描述2. 异常触发1) 参数级别约束2) 类级别约束3. 异常处理1) 字段级别约束

Python实现MQTT通信的示例代码

《Python实现MQTT通信的示例代码》本文主要介绍了Python实现MQTT通信的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 安装paho-mqtt库‌2. 搭建MQTT代理服务器(Broker)‌‌3. pytho

Java堆转储文件之1.6G大文件处理完整指南

《Java堆转储文件之1.6G大文件处理完整指南》堆转储文件是优化、分析内存消耗的重要工具,:本文主要介绍Java堆转储文件之1.6G大文件处理的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言文件为什么这么大?如何处理这个文件?分析文件内容(推荐)删除文件(如果不需要)查看错误来源如何避

MySQL进行数据库审计的详细步骤和示例代码

《MySQL进行数据库审计的详细步骤和示例代码》数据库审计通过触发器、内置功能及第三方工具记录和监控数据库活动,确保安全、完整与合规,Java代码实现自动化日志记录,整合分析系统提升监控效率,本文给大... 目录一、数据库审计的基本概念二、使用触发器进行数据库审计1. 创建审计表2. 创建触发器三、Java