共计 5920 个字符,预计需要花费 15 分钟才能阅读完成。
整顿自 RefactoringGuru
代码异味
——什么?代码如何“闻滋味”??
——它没有鼻子 … 但它必定会发臭!
代码收缩
【代码收缩】是代码、办法和类,它们的规模曾经减少到了难以解决的境地。通常,这些异味不会立刻呈现,而是随着程序的演变而积攒(尤其是当没有人致力铲除它们的时候)。
过长办法
办法蕴含的代码行太多。一般来说,任何超过十行的办法都会让你产生疑难。
过大的类
一个类蕴含许多字段 / 办法 / 代码行。
根本类型偏执
在简略工作中应用根本类型而不是小对象(例如货币、范畴、电话号码的非凡字符串等)
应用常量来编码信息(例如常量 USER_ADMIN_ROLE=1
示意具备管理员权限的用户。)
在数据数组中应用字符串常量作为字段名。
过长参数列表
一个办法有三个或四个以上的参数。
数据泥团
有时,代码的不同局部蕴含雷同的变量组(例如用于连贯数据库的参数)。这些组应该转化为它们本人的类。
面向对象滥用
所有这些异味都是面向对象编程原理的不残缺或不正确利用。
switch
语句
你有一个简单的 switch
运算符或 if
语句序列。
长期字段
长期字段仅在特定状况下获取其值(因而对象须要它)。除此之外,它们是空的。
被回绝的继承
如果子类只应用从其父类继承的一些办法和属性,那么层次结构就不失常。不须要的办法可能只是不应用,或者被从新定义并收回异样。
具备不同接口的备选类
两个类具备雷同的函数,但办法名不同。
更改的妨碍
这些异味意味着,如果你须要在代码的某个中央更改某些内容,那么你也必须在其余中央进行许多更改。因而,程序开发变得更加简单和低廉。
发散式更改
在更改类的时候,你发现自己必须更改许多不相干的办法。例如,增加新产品类型时,必须更改查找、展现和订购产品的办法。
散弹式更改
批改任何货色都须要对许多不同的类做出许多小的更改。
平行继承体系
每当你为一个类创立一个子类时,你就会发现自己须要为另一个类创立一个子类。
可有可无的货色
可有可无的货色是毫无意义和不必要的,如果没有它,代码就会更洁净、更高效、更容易了解。
正文
办法中充斥了解释性正文。
反复代码
两段代码看起来简直雷同。
冗余类
了解和保护类总是须要破费工夫和金钱。因而,如果一个类不足以吸引你的注意力,它应该被删除。
数据类
数据类是指只蕴含字段和用于拜访字段的办法(获取器和设置器)的类。这些只是其余类应用的数据容器。这些类不蕴含任何附加性能,并且不能独立操作它们所领有的数据。
死代码
变量、参数、字段、办法或类已不再应用(通常是因为它已过期)。
夸张通用性
存在未应用的类、办法、字段或参数。
耦合器
这一组中的所有异味都会导致类之间的适度耦合,或者显示如果耦合被适度委托所取代会产生什么。
性能依赖
一个办法拜访另一个对象的数据多于它本人的数据。
适度密切
一个类应用另一个类的外部字段和办法。
音讯链
在代码中能够看到一系列相似于 $a->b()->c()->d()
的调用。
中间人
如果一个类只执行一个操作,将工作委托给另一个类,那么它为什么存在呢?
其余异味
不欠缺的库类
库迟早会进行满足用户需要。因为库是只读的,所以问题的惟一解决方案,也就是更改库,通常是不可能的。
重构技巧
组合办法
很多重构都致力于正确地组合办法。在大多数状况下,过长的办法是万恶之源。这些办法中变幻莫测的代码暗藏了执行逻辑,使得该办法极难了解,甚至更难更改。
这一组中的重构技巧简化了办法,打消了代码反复,并为将来的改良铺平了路线。
提取办法
问题:你有一个能够组合在一起的代码片段。
解决方案:将此代码移动到一个独自的新办法(或函数),并用对该办法的调用替换旧代码。
内联函数
问题:当办法主体比办法自身更显著时,请应用此技巧。
解决方案:用办法的内容替换对办法的调用,并删除办法自身。
提取变量
问题:你的表达式很难了解。
解决方案:将表达式或其局部的后果放在独立的变量中,这些变量是自解释的。
内联长期变量
问题:你有一个长期变量,它被调配了一个简略表达式的后果,仅此而已。
解决方案:用表达式自身替换对变量的援用。
用查问替换长期变量
问题:将表达式的后果放在局部变量中,以便当前在代码中应用。
解决方案:将整个表达式挪动到一个独自的办法,并从中返回后果。查询方法,而不是应用变量。如有必要,在其余办法中退出新办法。
拆分长期变量
问题:你有一个局部变量,用于在办法中存储各种两头值(循环变量除外)。
解决方案:对不同的值应用不同的变量。每个变量应该只负责一个特定的事件。
移除参数赋值
问题:某些值被赋给了办法体中的参数。
解决方案:应用局部变量而不是参数。
用办法对象替换办法
问题:你有一个很长的办法,其中局部变量相互交织,以至于你不能利用【提取办法】。
解决方案:将该办法转换为一个独自的类,以便局部变量成为该类的字段。而后能够将该办法拆分为同一类中的多个办法。
代替算法
问题:所以你想用一个新的算法替换现有的算法?
解决方案:用新算法替换实现算法的办法体。
在对象间挪动性能
即便你在不同的类之间,以不太完满的形式散布了性能,依然存在心愿。
这些重构技术展现了如何在类之间平安地挪动性能,创立新的类,以及暗藏实现细节以防公开拜访。
挪动办法
问题:一个办法在另一个类中应用的次数多于在它本人的类中应用的次数。
解决方案:在应用该办法最多的类中创立一个新办法,而后将代码从旧办法挪动到这里。将旧办法的代码转换为对另一个类中新办法的援用,或者将其齐全删除。
挪动字段
问题:一个字段在另一个类中应用的次数比在它本人的类中应用的次数多。
解决方案:在新类中创立一个字段,并将旧字段的所有应用重定向到该字段。
提取类
问题:当一个类做两个类的工作时,会十分蠢笨。
解决方案:相同,创立一个新类,并将负责相干性能的字段和办法放在其中。
内联类
问题:一个类简直什么都不做,也不负责任何事件,也没有为它布局额定的责任。
解决方案:将所有性能从该类挪动到另一个类。
暗藏委托关系
问题:客户端从对象 A 的字段或办法中获取对象 B。而后客户端调用对象 B 的办法。
解决方案:在类 A 中创立一个新办法,将调用委托给对象 B。当初客户端不晓得也不依赖于类 B。
移除中间人
问题:一个类有太多的办法,这些办法只是委托给其余对象。
解决方案:删除这些办法,并强制客户端间接调用最终办法。
引入内部办法
问题:实用程序类不蕴含所需的办法,并且无奈将该办法增加到该类中。
解决方案:将该办法增加到客户端类,并将实用程序类的对象作为参数传递给它。
引入本地扩大
问题:实用程序类不蕴含你须要的某些办法。但不能将这些办法增加到类中。
解决方案:创立一个蕴含这些办法的新类,并使其成为实用程序类的子类或包装器。
组织数据
这些重构技术有助于数据处理,用丰盛的类性能替换根本类型。
另一个重要的后果是解开了类的关联,这使得类更具可移植性和可重用性。
自封装字段
问题:你间接拜访类内的公有字段。
解决方案:为字段创立一个获取器和设置器,并仅应用它们拜访字段。
用对象替换数据值
问题:一个类(或一组类)蕴含一个数据字段。该字段有本人的行为和相干数据。
解决方案:创立一个新类,将旧字段及其行为放在该类中,并将该类的对象存储在原始类中。
将值更改为援用
问题:所以你有单个类的许多雷同实例,并须要用单个对象替换它。
解决方案:将雷同的对象转换为单个援用对象。
将援用更改为值
问题:你有一个太小且很少更改的援用对象,因而无奈治理其生命周期。
解决方案:将其转化为值对象。
用对象替换数组
问题:你有一个蕴含各种类型数据的数组。
解决方案:将数组替换为每个元素都有独自字段的对象。
反复的被观测数据
问题:存储在类中的畛域数据是否负责 GUI?
解决方案:那么最好将数据分成不同的类,确保畛域类和 GUI 之间的连贯和同步。
将单向关联改为双向关联
问题:你有两个类,每个类都须要应用另一个类的性能,但它们之间的关联只是单向的。
解决方案:将短少的关联增加到须要它的类中。
将双向关联改为单向关联
问题:类之间存在双向关联,但其中一个类不应用另一个类的性能。
解决方案:删除未应用的关联。
用符号常量替换幻数
问题:你的代码应用了一个具备特定含意的数字。
解决方案:将这个数字替换为一个常量,该常量有一个人类可读的名称来解释数字的含意。
封装字段
问题:你有一个公共字段。
解决方案:将字段设置为公有,并为其创立拜访办法。
封装汇合
问题:一个类蕴含一个汇合字段和一个用于解决汇合的简略获取器和设置器。
解决方案:将获取器的返回值设为只读,并创立用于增加 / 删除汇合元素的办法。
用类替换类型代码
问题:一个类有一个蕴含类型代码的字段。这种类型的值不用于运算符条件,也不会影响程序的行为。
解决方案:创立一个新类,并应用其对象而不是类型代码的值。
用子类替换类型代码
问题:你有一个间接影响程序行为的代码类型(此字段的值触发条件中的各种代码)。
解决方案:为代码类型的每个值创立子类。而后将相干行为从原始类提取到这些子类中。用多态替换控制流代码。
用状态 / 策略替换类型代码
问题:你有一个影响行为的代码类型,但不能应用子类来打消它。
解决方案:用状态对象替换类型代码。如果须要用类型代码替换字段值,则另一个状态对象为“已插入”。
用字段替换子类
问题:你的子类只在(常量返回)办法上有所不同。
解决方案:用父类中的字段替换办法,并删除子类。
简化条件表达式
随着工夫的推移,条件的逻辑往往变得越来越简单,还有更多的技术能够解决这个问题。
合成条件
问题:你有一个简单的条件(if-then/else
或switch
)。
解决方案:将条件的简单局部合成为独自的办法:条件、then
和else
。
合并条件表达式
问题:你有多个条件产生雷同的后果或操作。
解决方案:将所有这些条件合并到一个表达式中。
合并反复的条件片段
问题:在条件语句的所有分支中都能够找到雷同的代码。
解决方案:将代码移到条件之外。
移除管制标记
问题:有一个布尔变量充当多个布尔表达式的管制标记。
解决方案:应用 break
、continue
和return
代替变量。
应用守卫子句来代替嵌套的条件判断
问题:有一组嵌套的条件,很难确定代码执行的失常流程。
解决方案:将所有非凡检查和边界状况隔离到独自的子句中,并将其放在次要查看之前。现实状况下,你应该有一个条件列表,一个接一个。
用多态替换条件
问题:你有一个条件,依据对象类型或属性执行各种操作。
解决方案:创立与条件的分支相匹配的子类。在它们中,创立一个共享方法,并将代码从条件的相应分支挪动到它。而后用相干的办法调用替换条件。后果是,依据对象类,能够通过多态实现正确的实现。
引入空对象
问题:因为一些办法返回 null
而不是实在对象,所以在代码中有很多 null
查看。
解决方案:返回一个显示默认行为的空对象,而不是null
。
引入断言
问题:要使局部代码失常工作,某些条件或值必须为true
。
解决方案:用特定的断言查看替换这些假如。
简化办法调用
这些技术使办法调用更简略、更容易了解。这反过来简化了用于类之间交互的接口。
重命名办法
问题:办法的名称不能解释该办法的性能。
解决方案:重命名该办法。
增加参数
问题:办法没有足够的数据来执行某些操作。
解决方案:创立一个新参数来传递必要的数据。
删除参数
问题:办法体中没有应用某个参数。
解决方案:删除未应用的参数。
将查问与批改离开
问题:是否有一个办法能够返回一个值,但也能够更改对象外部的某些内容?
解决方案:将该办法分为两种不同的办法。正如你所料,其中一个应该返回值,另一个则批改对象。
将办法参数化
问题:多个办法执行相似的操作,这些操作只在其外部值、数字或操作上有所不同。
解决方案:通过应用一个将传递必要非凡值的参数来组合这些办法。
用显式办法替换参数
问题:一个办法被分成几个局部,每个局部的运行取决于一个参数的值。
解决方案:将办法的各个局部提取到它们本人的办法中,并调用它们,而不是原始办法。
保留整个对象
问题:从一个对象中获取多个值,而后将它们作为参数传递给一个办法。
解决方案:相同,尝试传递整个对象。
用办法调用替换参数
问题:调用一个查询方法并将其后果作为参数传递给另一个办法,而该办法能够间接调用该查问。
解决方案:不要通过参数传递值,而是尝试在办法体中搁置一个查问调用。
引入参数对象
问题:你的办法蕴含一组反复的参数。
解决方案:用对象替换这些参数。
移除设置办法
问题:字段的值应该只在创立时设置,之后任何时候都不能更改。
解决方案:删除设置字段值的办法。
暗藏办法
问题:一个办法不被其余类应用,或者只在它本人的类层次结构中应用。
解决方案:将办法设置为公有或受爱护。
用工厂办法代替结构器
问题:你有一个简单的结构器,它的性能不仅仅是在对象字段中设置参数值。
解决方案:创立一个工厂办法并应用它替换结构器调用。
用异样替换错误代码
问题:办法返回批示谬误的非凡值?
解决方案:抛出一个异样。
用测试替换异样
问题:在一个简略的测试就能实现工作的中央抛出异样?
解决方案:用条件测试替换异样。
解决泛化
形象有本人的一组重构技术,次要对于沿着类继承层次结构挪动性能、创立新的类和接口、用委托代替继承以及相同。
上移字段
问题:两个类具备雷同的字段。
解决方案:从子类中删除字段,并将其挪动到超类。
上移办法
问题:你的子类具备执行相似工作的办法。
解决方案:使办法雷同,而后将它们挪动到相干的超类。
上移结构器主体
问题:你的子类的结构器的代码基本相同。
解决方案:创立一个超类结构器,并将子类中雷同的代码移动到它。在子类结构器中调用超类结构器。
下移办法
问题:超类中实现的行为是仅由一个(或几个)子类应用的吗?
解决方案:将此行为挪动到子类。
下移字段
问题:字段是否仅用于多数子类?
解决方案:将字段挪动到这些子类。
提取子类
问题:某个类具备仅在某些状况下应用的性能。
解决方案:创立一个子类,并在这些状况下应用它。
提取超类
问题:有两个类具备雷同的字段和办法。
解决方案:为它们创立一个共享超类,并将所有雷同的字段和办法挪动到其中。
提取接口
问题:多个客户端应用类接口的同一部分。另一种状况:两个类中的局部接口是雷同的。
解决方案:将这个雷同的局部挪动到它本人的接口。
折叠层次结构
问题:你有一个类层次结构,其中一个子类实际上与其超类雷同。
解决方案:合并子类和超类。
造成模板办法
问题:你的子类实现的算法蕴含程序雷同的相似步骤。
解决方案:将算法构造和雷同的步骤挪动到一个超类,并将不同步骤的实现留在子类中。
用委托替换继承
问题:有一个子类只应用其超类的一部分办法(或者不可能继承超类数据)。
解决方案:创立一个字段并在其中搁置一个超类对象,将办法委托给超类对象,并解脱继承。
用继承替换委托
问题:一个类蕴含许多简略的办法,这些办法将委托给另一个类的所有办法。
解决方案:使该类继承另一个类,这样就不须要委托办法。