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

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 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

python处理带有时区的日期和时间数据

《python处理带有时区的日期和时间数据》这篇文章主要为大家详细介绍了如何在Python中使用pytz库处理时区信息,包括获取当前UTC时间,转换为特定时区等,有需要的小伙伴可以参考一下... 目录时区基本信息python datetime使用timezonepandas处理时区数据知识延展时区基本信息

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

Java Response返回值的最佳处理方案

《JavaResponse返回值的最佳处理方案》在开发Web应用程序时,我们经常需要通过HTTP请求从服务器获取响应数据,这些数据可以是JSON、XML、甚至是文件,本篇文章将详细解析Java中处理... 目录摘要概述核心问题:关键技术点:源码解析示例 1:使用HttpURLConnection获取Resp

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

Java中Switch Case多个条件处理方法举例

《Java中SwitchCase多个条件处理方法举例》Java中switch语句用于根据变量值执行不同代码块,适用于多个条件的处理,:本文主要介绍Java中SwitchCase多个条件处理的相... 目录前言基本语法处理多个条件示例1:合并相同代码的多个case示例2:通过字符串合并多个case进阶用法使用