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

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通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

Tomcat版本与Java版本的关系及说明

《Tomcat版本与Java版本的关系及说明》:本文主要介绍Tomcat版本与Java版本的关系及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Tomcat版本与Java版本的关系Tomcat历史版本对应的Java版本Tomcat支持哪些版本的pythonJ

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN