`

重构_改善既有代码的设计中<<代码坏味道>>

 
阅读更多

 

书: 重构_改善既有代码的设计(马丁·福勒 中文完整版。

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

1.尽量消除重复的代码,将它们合而为一

根据重复的代码出现在不同的地方,分别采取不同的重构的策略:

在同一个Class的不同地方:通过采用重构工具提供的Extract Method功能提炼出重复的代码, 然后在这些地方调用上述提炼出方法。

在不同Subclasses中:通过Extract Method提炼出重复的代码,然后通过Pull Up Method将该方法移动到上级的Super class内。

在没有关系的Classes中:通过对其中一个使用Extract Class将重复的代码提炼到一个新类中,然后在另一个Class中调用生成的新类,消除重复的代码。

 

2.拆解过长的函数

过长的函数在我们的日常代码中经常可见,在C#中常通过#region #endregion区隔为不同的功能区域。

重构策略:通过Extract Method将过长的函数按照功能的不同进行适当拆解为小的函数,并且给这些小函数一个好名字。通过名字来了解函数提供的功能,提高代码的理解性。

 

3.拆解过大的类

过大的类也经常见到,特别是类中含有大量的成员变量。

重构策略:通过Extract Class将一些相关成员变量移植到新的Class中,如Employee类,一般会包含有联系方式的相关属性(电话, Mobile,地址,Zip等等),则可以将这些移植到新的EmployeeContact类中。

 

4.过长的参数列

过长的参数列的主要问题是难以理解,并且难以维护。如果要增加新的参数或者删除某一参数,易造成参数前后不一致。

重构策略:如果可以通过向已存在的对象查询获取参数,则可通过Replace Parameter with Method,移除参数列,通过在函数内部向上述已存在的对象查询来获取参数。

如果参数列中若干参数是已存在对象的属性,则可通过Preserve Whole Object将这些参赛替换为一个完整对象,这样不仅提高代码的可读性,同时已易于代码今后的维护。

另外,还可以将若干不相关的参数,使用Introduce Parameter Object来创建一个新的参数类。不过,我个人觉得如果这些情况过多的话,会产生很多莫名其妙的参数类了,反而降低代码的可读性。

 

 

5.Divergent Change(发散式变化)

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

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

 

6.Shotgun Surgery(霰弹式修改)

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

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

 

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

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

 

7.Feature Envy(依恋情结)

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

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

 

8.Data Clumps(数据泥团)

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

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

 

9.Primitive 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类中就可分别提炼出EmployeeName和EmployeeContact两个新类。

 

10.Switch Statements(Switch语句)

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

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

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

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

Source Class:

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

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

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

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

 

Superclass:

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

l         在Superclass中建立一个Abstract Query函数,用来获取Subclass的Type Code。

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

 

Subclass:

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

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

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

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

 

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

 

 

11.Parallel Inheritance Hierarchies(平行继承体系)

现象:为某个class增加一个subclass时,也必须为另一个class相应增加一个subclass。重构策略: 在一个class继承体系的对象中引用(refer to)另一个class继承体系的对象,然后运用Move Method和Move Field将被引用class中的一些方法和成员变量迁移宿主class中,消除被引用class的继承体系(注:这种平行继承体系好象比较少见也)。

 

12.Lazy Class(冗赘类)

现象:某一些class由于种种原因,现在已经不再承担足够责任,有些多余了。如同国有企业冗余人员一样,需要下岗了。

重构策略:通过Collapse Hierarchy,将这些冗余的class合并到superclass或subclass中,或者通过Inline Class(与Extract Class相反),将这些冗余class中的所有Method/Field迁移到其他相关的class中。

 

13.Speculative Generality(夸夸其谈未来性)

现象:系统中出现一些无用的abstract class,或者非必要的delegation(委托),或者多余的参数等等。

重构策略:分别使用Collapse Hierarchy合并abstract class,使用Inline Class移除非必要的delegation,使用Remove Parameter删除多余的参数。

 

14.Temporary Field(令人迷惑的暂时值域)

现象:class中存在一些Field,这些Field只在某种非常特定的情况下需要。

重构策略:通过Extract Class将这些孤独的Field及其相关的Method移植的一些新的Class中。提炼出来的新Class可能没有任何抽象意义,只是提供Method的调用,这些新Class一般称为Method Object。

 

15.Message Chains(过度耦合的消息链)

现象:向一个对象请求另一个对象,然后再向后者请求另一个对象,……,这就是Message Chain,意味着Message Chain中任何改变,将导致Client端不得不修改。

重构策略:通过Hide Delegate(隐藏委托关系)消除Message Chain,具体做法是在Message Chain的任何地方通过Extract Method建立一个简单委托(Delegation)函数,来减少耦合(Coupling)。

 

16.Middle Man(中间转手人)

现象:过度运用delegation,某个/某些Class接口有一半的函数都委托给其他class,这样就是过度delegation。

重构策略:运用Remove Middle Man,移除简单的委托动作(也就是移除委托函数),让client直接调用delegate受托对象。和上面的Hide Delegate(隐藏委托关系)刚好相反的过程。

 

由于系统在不断的变化和调整,因此[合适的隐藏程度]这个尺度也在相应的变化,Hide Delegate和Remove Middle Man重构策略可以系统适应这种变化。

 

另外,可保留一部分委托关系(delegation),同时也让Client也直接使用delegate受托对象。

 

17.Inappropriate Intimacy(狎昵关系)

现象:两个Class过分亲密,彼此总是希望了解对方的private成分。

重构策略:可以采用Move Method和Move Field来帮助他们划清界限,减少他们之间亲密行为。或者运用Change Bidirectional Association to Unidirectional,将双向关联改为单向,降低Class之间过多的依存性(inter-dependencies)。或者通过Extract Class将两个Class之间的共同点移植到一个新的Class中。

 

18.Alternative Classes with Different Interfaces(异曲同工的类)

现象:两个函数做相同的事情,却有不同的signature。

重构策略:使用Rename Method,根据他们的用途来重命名。另外,可以适当运用Move Method迁移某些行为,使Classes的接口保持一致。

 

19.Incomplete Library Class(不完美的程序库类)

现象:Library Class(类库)设计不是很完美,我们需要添加额外的方法。

重构策略:如果可以修改Library Class的Source Code,直接修改最好。如果无法直接修改Library Class,并且只想修改Library Class内的一两个函数,可以采用Introduce Foreign Method策略:在Client Class中建立一个函数,以外加函数的方式来实现一项新功能(一般而言,以server class实例作为该函数的第一个参数)。

 

如果需要建立大量的额外函数,可应该采用Introduce Local Extension:建立一个新class,使它包含额外函数,并且这个class或者继承或者wrap(包装)source class。

 

20.Data Class(纯稚的数据类)

现象:Data Class指:一些Class拥有Fields,以及用来访问Fields的getter/setter函数,但是没有其他的功能函数。(感觉这些Data Class如同Entity Class或Parameter Class,用来传递参数,我认为这种情况下没有必要重构。)

重构策略:找出其他class中访问Data Class中的getter/setter的函数,尝试以Move Method将这些函数移植到Data Class中,实现将数据和操作行为(方法)包装在一起,也让Data Class承担一定的责任(方法)。

 

21.Refused Bequest(被拒绝的遗赠)

现象:Subclass不想或不需要继承superclass的部分函数和Field。

重构策略:为subclass新建一个兄弟(sibling class),再运用Push Down Method和Push Down Field将superclass中的相应函数和Field下推到兄弟class,这样superclass就只包含subclass共享的东西了。其实,也就是将superclass中一些与特定的函数和Field放到特定的subclass中,superclass中仅包含subclass共享的函数和Field。

 

如果不想修改superclass,还可以运用Replace Inheritance with Delegation来达到目的。也就是以委托取代继承,在subclass中新建一个Field来保存superclass对象,去除subclass对superclass的继承关系,委托或调用superclass的方法来完成目的。

 

22.Comments(过多的注释)

现象:(晕倒,这个也要重构,Remove掉所有的Comments吗?不是。)当代码中出现一段长长的注释,一般是由于代码比较糟糕,需要进行重构,除去代码的坏味道。

重构策略:通过上面提及的各种重构策略,将代码的坏味道去除,使注释变成多余。

如果需要注释/解释一段代码做了什么,则可以试试Extract Method,提取出一个独立的函数,让函数名称解释该函数的用途/功能。另外,如果觉得需要注释来说明系统的某些假设条件,

也可尝试使用Introduce Assertion(引入断言),来明确标明这些假设。

 

当你感觉需要撰写注释时,请先尝试重构,试着让所有的注释都变得多余。

 

来自:  http://www.cnblogs.com/rickie/category/10512.html

 

 

 

分享到:
评论

相关推荐

    电子商务之价格优化算法:梯度下降:机器学习在价格优化中的角色.docx

    电子商务之价格优化算法:梯度下降:机器学习在价格优化中的角色.docx

    ToadforOracle与Oracle数据库版本兼容性教程.docx

    ToadforOracle与Oracle数据库版本兼容性教程.docx

    browser360-cn-stable-13.3.1016.4-1-amd64.deb

    360浏览器银河麒麟版 for X86 适配兆芯 / 海光 / intel / AMD CPU

    基于React.js和Material-UI个人作品集网站模板(附源码+说明文档).zip

    使用React.js构建,提供多种主题可供选择,并且易于定制。该项目旨在帮助开发者和自由职业者创建自己的个性化投资组合。 主要功能点 多种主题可供选择,包括绿色、黑白、蓝色、红色、橙色、紫色、粉色和黄色 易于定制,可以在src/data文件夹中更新个人信息 包含主页、关于、简历、教育、技能、经验、项目、成就、服务、推荐信、博客和联系等多个部分 支持通过Google表单收集联系信息 提供SEO优化建议 支持多种部署方式,如Netlify、Firebase、Heroku和GitHub Pages 技术栈主要 React.js Material-UI Axios React-fast-marquee React-helmet React-icons React-reveal React-router-dom React-router-hash-link React-slick Slick-carousel Validator

    中小型企业财务管理系统 SSM毕业设计 附带论文.zip

    中小型企业财务管理系统 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B

    apsw-3.38.5.post1-cp39-cp39-win_amd64.whl.rar

    python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。

    电子商务之价格优化算法:线性回归:价格优化策略实施.docx

    电子商务之价格优化算法:线性回归:价格优化策略实施.docx

    工业数字化转型的关键技术及其应用场景解析

    内容概要:报告详细介绍了企业数字化转型的驱动因素、数字化转型方案分类及其应用场景,重点关注了云计算、超连接、数字孪生、人工智能、分布式账本、增材制造、人机接口、数据共享、工业物联网等关键技术。这些技术不仅支持了企业的运营效率提升和业务模式创新,也为实现更快、更开放、更高效的数字化转型提供了支撑。报告最后提出了企业实施数字化转型的六个步骤。 适合人群:企业高级管理人员、技术人员、咨询顾问,以及对工业数字化转型感兴趣的读者。 使用场景及目标:帮助企业制定和实施数字化转型策略,优化运营模式,提升业务效率,增强市场竞争力。同时,也可作为政府部门、研究机构和行业协会的参考文献。 其他说明:报告中提到的关键技术及其应用场景对企业数字化转型具有重要的指导意义,特别是对于那些希望通过数字化转型实现业务创新和升级的企业。

    基于java的线上选课系统的设计与实现答辩PPT.pptx

    基于java的线上选课系统的设计与实现答辩PPT.pptx

    原版aggdraw-1.3.15-cp311-cp311-win_arm64.whl-下载即用直接pip安装.zip

    安装前的准备 1、安装Python:确保你的计算机上已经安装了Python。你可以在命令行中输入python --version或python3 --version来检查是否已安装以及安装的版本。 个人建议:在anaconda中自建不同python版本的环境,方法如下(其他版本照葫芦画瓢): 比如创建python3.8环境,anaconda命令终端输入:conda create -n py38 python==3.8 2、安装pip:pip是Python的包管理工具,用于安装和管理Python包。你可以通过输入pip --version或pip3 --version来检查pip是否已安装。 安装WHL安装包 1、打开命令行(或打开anaconda命令行终端): 在Windows上,你可以搜索“cmd”或“命令提示符”并打开它。 在macOS或Linux上,你可以打开“终端”。 2、cd到whl文件所在目录安装: 使用cd命令导航到你下载的whl文件所在的文件夹。 终端输入:pip install xxx.whl安装即可(xxx.whl指的是csdn下载解压出来的whl) 3、等待安装完成: 命令行会显示安装进度,并在安装完成后返回提示符。 以上是简单安装介绍,小白也能会,简单好用,从此再也不怕下载安装超时问题。 使用过程遇到问题可以私信,我可以帮你解决! 收起

    电子商务之价格优化算法:贝叶斯定价:贝叶斯网络在电子商务定价中的应用.docx

    电子商务之价格优化算法:贝叶斯定价:贝叶斯网络在电子商务定价中的应用.docx

    IMG_20241105_235746.jpg

    IMG_20241105_235746.jpg

    基于java的毕业设计选题系统答辩PPT.pptx

    基于java的毕业设计选题系统答辩PPT.pptx

    专升本考试资料全套.7z

    专升本考试资料全套.7z

    Trustwave DbProtect:数据库活动监控策略制定.docx

    Trustwave DbProtect:数据库活动监控策略制定.docx

    VB程序实例-CD-ROM开关.zip

    基于VB的程序实例,可供参考学习使用

    课设毕设基于SpringBoot+Vue的教育资源共享平台源码可运行.zip

    本压缩包资源说明,你现在往下拉可以看到压缩包内容目录 我是批量上传的基于SpringBoot+Vue的项目,所以描述都一样;有源码有数据库脚本,系统都是测试过可运行的,看文件名即可区分项目~ |Java|SpringBoot|Vue|前后端分离| 开发语言:Java 框架:SpringBoot,Vue JDK版本:JDK1.8 数据库:MySQL 5.7+(推荐5.7,8.0也可以) 数据库工具:Navicat 开发软件: idea/eclipse(推荐idea) Maven包:Maven3.3.9+ 系统环境:Windows/Mac

    基于Thinkphp5框架的Java插件设计源码

    该源码项目是一款基于Thinkphp5框架的Java插件设计,包含114个文件,其中Java源文件60个,PNG图片32个,XML配置文件7个,GIF图片7个,Git忽略文件1个,LICENSE文件1个,Markdown文件1个,Xmind文件1个,Idea项目文件1个,以及JAR文件1个。

    数据库开发和管理最佳实践.pdf

    数据库开发和管理最佳实践.pdf

    课设毕设基于SpringBoot+Vue的农场投入品运营线上管理系统源码可运行.zip

    本压缩包资源说明,你现在往下拉可以看到压缩包内容目录 我是批量上传的基于SpringBoot+Vue的项目,所以描述都一样;有源码有数据库脚本,系统都是测试过可运行的,看文件名即可区分项目~ |Java|SpringBoot|Vue|前后端分离| 开发语言:Java 框架:SpringBoot,Vue JDK版本:JDK1.8 数据库:MySQL 5.7+(推荐5.7,8.0也可以) 数据库工具:Navicat 开发软件: idea/eclipse(推荐idea) Maven包:Maven3.3.9+ 系统环境:Windows/Mac

Global site tag (gtag.js) - Google Analytics