重构(Refactoring)技巧读书笔记 之二

重构(Refactoring)技巧读书笔记 之二<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

General Refactoring Tips, Part 2

 

本文继续《重构(Refactoring)技巧读书笔记 之一》,重构的确是未来软件工程师需要掌握的一项技能。目前一些支持.Net的重构工具,如ReSharper for VS.Net v1.0Borland Together for VS.Net v2.0VS.Net 2005等,只能支持一些有限的、比较简单的重构策略。大量的重构策略需要软件工程师清晰的了解,人工为主,运用重构工具辅助进行。(注:本文重构策略的名称及其大部分内容来自《重构-改善既有代码的设计》一书,Martin Fowler 著,侯捷等译)。

下面的内容延续上一节的内容,其中提及的一些代码坏味道Bad Smell in Codes及其重构策略相对而言要比较麻烦一些。

 

一、代码坏味道(Bad Smell in Codes)及其重构策略

5Divergent Change(发散式变化)

现象:当某个Class因为外部条件的变化或者客户提出新的功能要求等时,每次修改要求我们更新Class中不同的方法。不过这种情况只有在事后才能觉察到,因为修改都是在事后发生的么(废话)。

重构策略:将每次因同一条件变化,而需要同时修改的若干方法通过Extract Class将它们提炼到一个新Class中。实现目标是:每次变化需要修改的方法都在单一的Class中,并且这个新的Class内所有的方法都应该与这个变化相关。

 

6Shotgun Surgery(霰弹式修改)

现象:当外部条件发生变化时,每次需要修改多个Class来适应这些变化,影响到很多地方。就像霰弹一样,发散到多个地方。

重构策略:使用Move MethodMove FieldClass中需要修改的方法及成员变量移植到同一个Class中。如果没有合适的Class,则创建一个新Class。实现目标是,将需要修改的地方集中到一个Class中进行处理。

 

比较Divergent Change(发散式变化)和Shotgun Surgery(霰弹式修改):

前者指一个Class受到多种外部变化的影响。而后者指一种变化需要影响到多个Class需要修改。都是需要修理的对象。

 

7Feature Envy(依恋情结)

现象:Class中某些方法“身在曹营心在汉”,没有安心使用Class中的成员变量,而需要大量访问另外Class中的成员变量。这样就违反了对象技术的基本定义:将数据和操作行为(方法)包装在一起。

重构策略:使用Move Method将这些方法移动到对应的Class中,以化解其“相思之苦”,让其牵手。

 

8Data Clumps(数据泥团)

现象:指一些相同数据项目(Data Items),如Class成员变量和方法中参数列表等,在多个Class中多次出现,并且这些数据项目有其内在的联系。

重构策略:通过使用Introduce Parameter Object(创建新的参数对象取代这些参数)或Preserve Whole Object(使用已存在的对象取代这些参数),实现使用对象代替Class成员变量和方法中参数列表,清除数据泥团,使代码简洁,也提高维护性和易读性。

 

9Primitive Obsession(基本型偏执狂)

现象:在Class中看到大量的基本型数据项目(Data Item),如Employee类中有大量的数据成员,Employee#, FirstName, MiddleName, LastName, Address, State, City, Street, Zip, OfficePhone, CellPhone, Email……等等。

重构策略:使用Extract Class(提炼新类)或Preserve Whole Object(使用已存在的对象取代这些参数),实现使用对象代替基本型数据项目(Data Item)。如上述Employee类中就可分别提炼出EmployeeNameEmployeeContact两个新类。

 

10Switch StatementsSwitch语句)

现象:同样的Switch语句出现在不同的方法或不同的Class中,这样当需要增加新的CASE分支或者修改CASE分支内语句时,就必须找到所有的地方,然后进行修改。这样,就比较麻烦了。

重构策略:(1)首先采用Extract MethodSwitch语句提炼到一个独立的函数。

(2)然后以Move Method搬移到需要多态性(Polymorphism)的Superclass里面或者是构建一个新的Superclass

(3)进一步使用Replace Type Code with Subclasses或者Replace Type Code with State/Strategy。这步就比较麻烦些,不过记住如下基本规则:这里一般有3Class分别为Source ClassSuperclassSubclass

Source Class

l         使用Self Encapsulate Field,将Type Code成员变量封装起来,也就是建立对应的Setter/Getter函数。

l         Source Class中增加一个Superclass类型的成员变量,用来存放Subclass实例对象。

l         Source Class中的Getter函数,通过调用SuperclassAbstract Query函数来完成。

l         Source Class中的Setter函数,通过调用Superclass中的Static工厂化方法来获取合适的Subclass实例对象。

 

Superclass

新建的一个Class(注:就是上面通过Move Method搬移生成的Superclass),根据Type Code的用途命名该Class,作为Superclass

l         Superclass中建立一个Abstract Query函数,用来获取SubclassType Code

l         Superclass中创建Static工厂化方法生产对应的Subclass对象,这里会存在一个Switch语句(不要再动脑筋来重构这个Switch语句了,这个Switch语句不会在多处重复存在,并且这里用于决定创建何种Subclass对象,这是完全可以接受的)。

 

Subclass

l         根据每一个Switch/Type分支,建立对应的Subclass,并且Subclass的命名可以参考Switch/Type分支的命名。

l         在每一个Subclass中重载SuperclassAbstract Query函数,返回特定的Type Code

(4)现在Superclass仍然存在Switch分支,是时候轮到Replace Conditional with Polymorphism上场了。具体而言,就是在每一个Subclass中创建重载方法(注:该方法是Superclass中含有Switch语句的方法),并将SuperclassSwitch语句对应的Case分支剪切过来。最后将Superclass中该方法初象化Abstract,并清除Switch语句及其所有的Case分支。

这样就完成了整个重构过程,这个比较麻烦。

 

注:并不是一看到Switch语句及CASE分支,就马上/偏执狂采用上述重构策略进行重构,画蛇添足或吃亏不讨好(个人观点)。一般而言,只有看到多处出现相同的Switch语句时,才应该考虑进行重构。

 

References:

1, Rickie, 重构(Refactoring)技巧读书笔记 之一

转载于:https://www.cnblogs.com/rickie/archive/2004/10/04/48859.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/277366.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

史上最全的Angular.js 的学习资源

Angular.js 的一些学习资源 基础 官方&#xff1a; http://docs.angularjs.org angularjs官方网站已被墙&#xff0c;可看 http://www.ngnice.com/&#xff1b;官方zip下载包 http://best.factj.com/dolymood/angular-packages&#xff0c;已增加docs服务&#xff0c;输入地址即…

BMP位图之8位位图(三)

起始结构 typedef struct tagBITMAPFILEHEADER { WORD bfType; //类型名&#xff0c;字符串“BM”&#xff0c; DWORD bfSize; //文件大小 WORD bfReserved1; //保留字 WORD bfReserved2; //保留字 DWORD bfOffBits; //实际位图数据的偏移字节数&#xff0c;即前三个部分长度之…

DNN 汉化中的问题????

今天看到了一份已经汉化过的DNN但是比较奇怪&#xff0c;当第一次运行后我所指定的新数据库中并没有添加新的内容&#xff0c;但是网站上的确是已经汉化过了的&#xff0c;不知道它把汉化的内容放到了哪里&#xff1f;&#xff1f;&#xff1f; 另外他所汉化界面的地方&#x…

php 打印对象详细信息,php打印显示数组与对象的函数详解

php打印显示数组与对象的函数详解发布于 2014-11-17 18:55:49 | 699 次阅读 | 评论: 0 | 来源: 网友投递PHP开源脚本语言PHP(外文名: Hypertext Preprocessor&#xff0c;中文名&#xff1a;“超文本预处理器”)是一种通用开源脚本语言。语法吸收了C语言、Java和Perl的特点&…

ios开发-调用系统自带手势

在 iPhone 或 iPad 的开发中&#xff0c;除了用 touchesBegan / touchesMoved / touchesEnded 这组方法来控制使用者的手指触控外&#xff0c;也可以用 UIGestureRecognizer 的衍生类別来进行判断。用 UIGestureRecognizer 的好处在于有现成的手势&#xff0c;开发者不用自己计…

Node.js 事件循环

Node.js 事件循环 Node.js 是单进程单线程应用程序&#xff0c;但是因为 V8 引擎提供的异步执行回调接口&#xff0c;通过这些接口可以处理大量的并发&#xff0c;所以性能非常高。 Node.js 几乎每一个 API 都是支持回调函数的。 Node.js 基本上所有的事件机制都是用设计模式中…

全国翻译专业资格(水平)考试

http://www.spta.gov.cn/moreksxx.jsp?lmCodeA02010205转载于:https://www.cnblogs.com/Danilo/archive/2004/10/31/58821.html

linux文件句柄,【LINUX】使用lsof处理文件恢复、句柄以及空间释放问题

曾经在生产上遇到过一个df 和 du出现的结果不一致的问题&#xff0c;为了排查到底是哪个进程占用了文件句柄&#xff0c;导致空间未释放&#xff0c;首先在linux上面&#xff0c;一切皆文件&#xff0c;这个问题可以使用lsof这个BT的命令来处理(这个哈还可以来查询文件句柄泄露…

android天气查询(二)之网络json数据的获取

前面一篇文章介绍了如何使用ksoap获取天气信息&#xff0c;但是使用的网络资源受到了限制&#xff0c;所以我们这里会采用第二种方法&#xff0c;可以无限制的获取。http://m.weather.com.cn/data/101010100.html 但是对应的101010100(北京)我们怎么获取呢&#xff0c;还有就是…

累.....

今天我真的蔫了&#xff0c;好累&#xff0c;脑子也好浊&#xff0c;但是还好&#xff0c;最终达到了预期的目的。我终于把henry的dataGrid实现了&#xff0c;犯了低级错误&#xff0c;和好多人讨论&#xff0c;但最终还是henry解决的。那一刻&#xff0c;真的好爽&#xff0c;…

001-pro ant design 升级2.0后变更

一、更新点 1、目录调整 2、本地代理服务器调整 roadhog→umi 配置方式 在这个config/config.js配置 "proxy": { "/api": { "target": "http://jsonplaceholder.typicode.com/", "changeOrigin": true, "pathRewrite&q…

linux cp 时 略过文件,CentOS下执行cp命令式提示略过文件夹

今天在CentOS下复制一个文件夹到另一个文件夹的时候cp ./res /usr 的时候出现了问题&#xff0c;提示我的是&#xff1a;cp略过了文件夹后来我找了一下在网上search了一下CP命令的用法&#xff1a;CP命令该命令的功能是将给出的文件或目录拷贝到另一文件或目录中&#xff0c;同…

属性页中的ON_UPDATE_COMMAND_UI

我前面翻译了一篇文章简单的谈了一下在对话框处理ON_UPDATE_COMMAND_UI 消息。又在www.codeguru.com上看到在属性页中处理ON_UPDATE_COMMAND_UI 消息的方法和在对话框中稍有不同。两者的处理大体上一样。只是在属性页中还需要一个步骤。需要从CPropertySheet派生类&#xff0c;…

linux基础命令rpm,rpm常用命令集合1

提要&#xff1a;RPM 是 Red Hat Package Manager 的缩写&#xff0c;原意是Red Hat 软件包管理&#xff1b;本文介绍RPM&#xff0c;并结合实例来解说RPM手工安装、查询等应用&#xff1b;正文&#xff1a;RPM包管理的用途&#xff1b;1、可以安装、删除、升级和管理软件&…

hibernate详解

Hibernate原理与应用 主要内容 1、引入 2、安装配置 3、基本概念和CURD 4、HQL和Criteria 5、关联映射 6、继承映射 7、集合映射 8、懒加载 9、缓存 10、事务 11、其他 12、Hibernate不适合的场景 13、与JPA的集成(annotation方式) 14、最佳实践 1、引入 模型不匹配(阻抗不匹配…

Csharp+Asp.net系列教程(四)

迈克老猫 来自&#xff1a;老猫的理想 本教程参考C#和ASP.NET程序设计教程撰写&#xff0c;有什么不足之处请大家指出&#xff0c;或在老猫的理想BLOG留言。 这次简述一下操作符 1.算术操作符 算术操作符包括加()、减(-)、乘(*)、除(/)和求余(%)。加减操作符…

linux 脚本 写更新,用Shell写的游戏客户端更新脚本

#!/bin/sh########################################### 客户端版本更新系统########################################### CLIENT_INIT_VERSION 客户端新版本&#xff0c;默认是0# CLIENT_OLD_VERSION 客户端旧版本(存放在VERSION_FILE指定的文件中)&#xff0c;当没有时取CL…

Android Studio 环境搭建参考,jdk10javac命令提示不是内部或外部命令

https://blog.csdn.net/qq_33658730/article/details/78547789 win10下Android Studio和SDK下载、安装和环境变量配置 http://yanmin99.com/ android-0基础从入门到精通 环境变量ANDROID_HOMEE:\Program Files (x86)\Android\SdkJAVA_HOMEE:\Program Files\Java\jdk1.8.0_161TO…

报表引擎 - 数据模型

介绍 本文档是报表模型的数据模型部分&#xff0c;说明平台报表中涉及的报表数据相关 术语 名称 说明 数据源 数据源是与数据存储的连接。支持数据源适配 数据集 定义报表数据来源的一个二维表 参数 报表往往有参数&#xff0c;例如&#xff0c;日报表&#xff0c;当…

linux 删旧内核,Ubuntu 删除旧内核的方法

使用Ubuntu时如果系统里安装了很多内核&#xff0c;会造成多于的启动列表。想删掉一些不用的内核方法如下方法一&#xff1a;首先就是使用如下命令&#xff0c;列出所有安装的内核&#xff0c;下表中&#xff0c;带有p_w_picpath的就是内核文件。从中选择要卸载的包&#xff0c…