
重构
文章平均质量分 57
疯狗的袋鼠
醉逍遥
展开
-
关于重构(Refactoring)
大原创 2021-07-11 21:08:49 · 2122 阅读 · 0 评论 -
代码的坏味道
Duplicated Code(重复的代码)坏味道行列中首当其冲的就是Duplicated Code。如果你在一个以上的地点看到相同的程序结构,那么可以肯定:设法将它们合而为一,程序会变得更好。最单纯的Duplicated Code就是“同一个类的两个函数含有相同的表达式”。这时候你需要做的就是采用Extract Method提炼出重复的代码,然后让这两个地点都调用被提炼出来的那一段代码。另一种常见的情况就是“两个互为兄弟的子类内含相同的表达式”。要避免这种情况,只需对两个类都是用Extract Me原创 2021-09-16 15:04:02 · 1168 阅读 · 1 评论 -
Push Down Field(字段下移)
动机Push Down Field与Pull Up Field恰恰相反;如果只有某些(而非全部)子类需要超类内的一个字段,你可以使用本项重构。做法在所有子类中声明该字段。将该字段从超类中移除。编译,测试。将该字段从所有不需要它的那些子类中删除。编译,测试。范例重构后...原创 2021-08-23 16:40:49 · 203 阅读 · 0 评论 -
Push Down Method(函数下移)
动机Push Down Method与Pull Up Method恰恰相反。当我有必要把某些行为从超类移至特定的子类时,我就使用Push Down Method,它通常也只有这种时候有用。使用Extract Subclass之后你可能会需要它。做法在所有子类中声明该函数,将超类中的函数本体复制到每一个子类函数中。删除超类中的函数。2.1. 你可能必须修改调用端的某些变量声明或参数声明,以便能够使用子类。2.2. 如果有必要通过一个超类对象访问该函数,或你不想把该函数从任何子类中移除,再或超类是原创 2021-08-21 10:44:25 · 539 阅读 · 0 评论 -
Pull Up Constructor Body(构造函数本体上移)
动机如果你看见各个子类中的函数有共同行为,第一个念头应该是将共同行为提炼到一个独立函数中,然后将这个函数提升到超类。对构造函数而言,它们彼此的共同行为往往就是“对象的建构”,这时候你需要在超类中提供一个构造函数,然后子类都来调用它。很多时候,子类构造函数的唯一动作就是调用超类构造函数。这里不能运用Pull Up Method,因为你无法在子类中继承超类构造函数。如果重构过程过于复杂,你可以考虑转而使用Replace Constructor with Factory Method。做法在超类中定义一原创 2021-08-20 10:58:32 · 431 阅读 · 0 评论 -
Pull Up Method(函数上移)
动机避免行为重复是很重要的。尽管重复的两个函数也可以各自工作得很好,但重复自身只会成为错误的滋生地,此外别无价值。无论何时,只要系统之内出现重复,你就会面临“修改其中一个却未能修改另一个”的风险。通常,找出重复也有一定困难。如果某个函数在各个子类中的函数体都相同(它们很可能是通过复制粘贴得到的),这就是最显而易见的Pull Up Method适用场合。当然,情况并不总是如此明显。你也可以只管放心地重构,再看看测试程序会不会发牢骚,但这就需要对你的测试有充分的信心。我发现,观察这些可能重复的函数之间的差异原创 2021-08-19 10:27:44 · 846 阅读 · 0 评论 -
Pull Up Field(字段上移)
动机如果各子类时分别开发的,或者是在重构过程中组合起来的,你常会发现它们拥有重复特性,特别是字段更容易重复。这样的字段有时拥有近似的名字,但也并非绝对如此。判断若干字段是否重复,唯一的办法就是观察函数如何使用它们。如果它们被使用的方式很相似,你就可以将它们归纳到超类去。本项重构从两方面减少重复:首先它去除了重复的数据声明:其次它使你可以将使用该字段的行为从子类移至超类,从而去除重复的行为。做法针对待提升之字段,检查它们的所有被调用点,确认它们以同样的方式被使用。如果这些字段的名称不同,先将它们改原创 2021-08-18 10:36:01 · 309 阅读 · 0 评论 -
Replace Constructor with Factory Method(以工厂函数取代构造函数)
动机使用该项重构的最显而易见的动机,就是在派生子类的过程中以工厂函数取代类型码。你可能常常需要根据类型码创建相应的对象,现在,创建名单上还得加上子类,那些子类也是根据类型码来创建。然而由于构造函数只能返回单一类型的对象,因此你需要将构造函数替换为工厂函数。此外,如果构造函数的功能不能满足你的需要,也可以使用工厂函数来代替它。工厂函数也是Change Value to Reference的基础。你也可以令你的工厂函数根据参数的个数和类型,选择不同的创建行为。做法新建一个工厂函数,让它调用现在的构造函原创 2021-08-17 10:37:58 · 594 阅读 · 0 评论 -
Encapsulate Downcast(封装向下转型)
动机在强类型OO语言中,向下转型是最烦人的事情之一。之所以很烦人,是因为从感觉上来说它完全没有必要:你竟然越俎代庖地告诉编译器某些应用由编译器自己计算出来的东西。但是,由于计算对象类型往往比较麻烦,你还是常常需要亲自告诉编译器对象的确切类型。向下转型在java特别盛行,因为java没有模板机制,因此如果你想从集合之中取出一个对象,就必须进行向下转型。向下转型也许是一种无法避免的罪恶,但你仍然应该尽可能少做。如果你的某个函数返回一个值,并且你知道所返回的对象类型比函数签名所昭告的更特化,你便是在函数用户身原创 2021-08-16 11:36:41 · 199 阅读 · 0 评论 -
Hide Method(隐藏函数)
动机重构往往促使你修改函数的可见度。提高函数可见度的情况很容易想象:另一个类需要用到某个函数,因此你必须提高该函数的可见度。但是要指出一个函数的可见度是否过高,就稍微困难一些。理想状态下,你可以使用工具检查所有函数,指出可被隐藏起来的函数。即使没有这样的工具,你也可以进行这样的检查。一种特别常见的情况是:当你面对一个过于丰富、提供了过多行为的接口时,就值得将非必要的取值函数和设值函数隐藏起来。尤其当你面对的是一个只有简单封装的数据容器时,情况更是如此。随着越来越多行为被放入这个类,你会发现许多取值/设值原创 2021-08-15 11:06:16 · 279 阅读 · 0 评论 -
Remove Setting Method(移除设值函数)
动机如果你为某个字段提供了设值函数,这就暗示这个字段值可以被改变。如果你不希望在对象创建之后此字段还有机会被改变,那就不要为它提供设值函数(同时将该字段设为final)。这样你的意图会更加清晰,并且可以排除其值被修改的可能性——这种可能性往往是非常大的。如果你保留了间接访问变量的方法,就可能经常有程序员盲目使用它们。这些人甚至会在构造函数中使用设值函数!我猜想他们或许是为了代码的一致性,但却忽视了设值函数往后可能带来的混淆。做法检查设值函数被使用的情况,看他是否只被构造函数调用,或者被构造函数所调原创 2021-08-14 15:55:25 · 264 阅读 · 0 评论 -
Replace Parameter with Method(以函数取代参数)
动机如果函数可以通过其他途径获得参数值,那么它就不应该通过参数取得该值。过长的参数列会增加程序阅读者的理解难度,因此我们应该尽可能缩短参数列的长度。缩减参数列的办法之一就是:看看参数接收端是否可以通过与调用端相同的计算来取得参数值。如果调用端通过其所属对象内部的另一个函数来计算参数,并在计算过程中未曾引用调用端的其他参数,那么你就应该可以将这个计算过程转移到被调用端内,从而去除该项参数。如果你所调用的函数隶属另一个对象,而该对象拥有调用端所属对象的引用,前面所说的这些也同样适用。但是,如果参数值的计算原创 2021-08-13 11:19:54 · 477 阅读 · 0 评论 -
Preserve Whole Object(保持对象完整)
动机有时候,你会将来自同一对象的若干项数据作为参数,传递给某个函数。这样做的问题在于:万一将来被调用函数需要新的数据项,你就必须查找并修改对此函数的所有调用。如果你把这些数据所属的整个对象传给函数,可以避免整个尴尬的处境,因为被调用函数可以向那个参数对象请求任何它想要的信息。除了可以使参数列更稳固之外,Preserve Whole Object往往还能提高代码的可读性。过长的参数列很难使用,因为调用者和被调用者都必须记住这些参数的用途。此外,不使用完整对象也会造成重复代码,因为被调用调用函数无法利用完整原创 2021-08-11 11:03:55 · 554 阅读 · 0 评论 -
Parametaerize Method(令函数携带参数)
动机你可能会发现这样的两个函数:它们做着类似的工作,但因少数几个值致使行为略有不同。在这种情况下,你可以将这些各自分离的函数统一起来,并通过参数来处理那些变化情况,用以简单问题。这样的修改可以去除重复的代码,并提高灵活性,因为你可以用这个参数处理更多的变化情况。做法新建一个带有参数的函数,使它可以替换先前所有的重复性函数。编译。将调用旧函数的代码改为调用新函数。编译,测试。对所有旧函数重复上述步骤,每次替换后,修改并测试。范例class Employee { void tenPerc原创 2021-08-09 11:18:54 · 199 阅读 · 0 评论 -
Separate Query from Modifier(将查询函数和修改函数分离)
动机如果某个函数只是向你提供一个值,没有任何看得到的副作用,那么这是个很有价值的东西。你可以任意调用这个函数,也可以把调用动作搬到函数的其他地方。简而言之,需要操心的事情少多了。明确表现出“有副作用”与“无副作用”两种函数之间的差异,是个很好的想法。下面是一条好规则:任何有返回值的函数,都不应该有看得到的副作用。有些程序员甚至将此作为一条必须遵守的规则。就像对待任何东西一样,我并不绝对遵守它,不过我总是尽量遵守,而它也回报我很好的效果。做法新建一个查询函数,令它返回的值与原函数相同。修改原函数,原创 2021-08-08 11:29:17 · 298 阅读 · 0 评论 -
Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件表达式)
动机根据我的经验,条件原创 2021-08-03 20:00:57 · 167 阅读 · 0 评论 -
Consolidate Duplicate Conditional Fragments(合并重复的条件片段)
动机有时你会发现,一组条件表达式的所有分支都执行了相同的某段代码。如果是这样,你就应该将这段代码搬移到条件表达式外面。这样,代码才能更清楚地表明那些东西随条件的变化而变化、哪些东西保持不变。做法鉴别出“执行方式不随条件变化而变化”的代码。如果这些共通代码位于条件表达式起始处,就将它移到条件表达式之前。如果这些共通代码位于条件表达式尾端,就将它移到条件表达式之后。如果这些共通代码位于条件表达式中段,就需要观察共通代码之前或之后的代码是否改变了什么东西。如果的确有所改变,应该首先将共通代码向前或向原创 2021-07-30 09:28:56 · 316 阅读 · 0 评论 -
Consolidate Conditional Expression(合并条件表达式)
动机有时你会发现这样一串条件检查:检查条件各不相同,最终行为却一致。如果发现这种情况,就应该使用“逻辑或”和“逻辑与”将它们合并成为一个条件表达式。之所以要合并条件代码,有两个重要的原因。首先,合并后的条件代码会告诉你“实际上只有一次条件检查”,只不过有多个并列条件需要检查而已“,从而使这一次检查的用意更清晰。当然,合并前和合并后的代码有着相同的效果,但原先代码传达出的信息却是”这里有一些各自独立的条件测试,它们知识恰好同时发生“。其次,这项重构往往可以为你使用Extract Method做好准备。将检原创 2021-07-29 15:43:35 · 339 阅读 · 0 评论 -
Decompose Conditional(分解条件表达式)
动机程序之中,复杂得条件逻辑是最常导致复杂度上升得地点之一。你必须编写代码来检查不同的条件分支、根据不同的分支做不同的事,然后,你很快就会得到一个相当长的函数。大型函数自身就会使代码的可读性下降,而条件逻辑则会使代码更难阅读。在带有复杂条件逻辑的函数中,代码(包括检查条件分支的代码和真正实现功能的代码)会告诉你发生的事,但常常让你弄不清楚为什么会发生这样的事,这就说明代码的可读性的确大大降低了。和任何大块代码一样,你可以将它分解为多个独立函数,根据每个小块代码的用途,为分解而得的新函数命名,并将原函数中原创 2021-07-28 19:11:49 · 334 阅读 · 0 评论 -
Encapsulate Collection(封装集合)
动机我们常常会在一个类中使用集合(可能是array、list、set或vector)来保存一组实例。这样的类通常也会提供针对该集合的取值/设值函数。但是,集合的处理方式应该和其他种类的数据略有不同。取值函数不该返回集合自身,因为这会让用户得以修改集合内容而集合拥有者却一无所悉。这也会对用户暴露过多内部数据结构的信息。如果一个取值函数确实需要返回多个值,它应该避免用户直接操作对象内所保存的集合,并隐藏对象内于用户无关的数据结构。另外,不因该为整个集合提供一个设值函数,但应该提供用以为集合添加/移除元素的原创 2021-07-24 10:23:06 · 219 阅读 · 1 评论 -
Encapsulate Field(封装字段)
动机面向对象的首要原则之一就是封装,或者称为“数据隐藏”。按此原则,你绝不应该将数据声明为public,否则其他对象就有可能访问甚至修改这项数据,而拥有该数据的对象却毫无察觉。于是,数据和行为就被分开了——这可不是件好事。数据声明为public被看作是一种不好的做法,因为这样会降低程序的模块化程度。数据和使用该数据的行为如果集中在一起,一旦情况发生变化,代码的修改就会比较简单,因为需要修改的代码都集中于同一块地方,而不是星罗棋布地散落在整个程序中。做法为public字段提供取值/设值函数。找到这原创 2021-07-23 10:20:08 · 400 阅读 · 0 评论 -
Replace Magic Number with Symbolic Constant(以字面常量取代魔法数)
动机在计算机科学中,魔法数是历史最悠久得不良现象之一。所谓魔法数是指拥有特殊意义,却又不能明确表现出这种意义得数字。如果你需要在不同的地方引用同一个逻辑数,魔法数会让你烦恼不已,因为一旦这些数发生变化,你就必须在程序中找到所有魔法数,并将它们全部修改一遍,这简直就是一场噩梦。就算你不需要修改,要准确找出每个魔法数的用途,也会让你颇费脑筋。许多语言都允许你声明变量。变量不会造成任何性能开销,却可以大大提高代码的可读性。做法声明一个常量,令其值为原本的魔法数值。找出这个魔法数的所有引用点。检查是否原创 2021-07-21 09:45:58 · 342 阅读 · 0 评论 -
Replace Array with Object(以对象取代数组)
动机数组是一种常见的用以组织数据的结构。不过,它们应该只用于“以某种顺序容纳一组相似对象”。有时候你会发现,一个数组容纳了多种对象,这会给用户带来麻烦,因为他们很难记住像”数组的第一个元素是人名“这样的约定。对象就不同了,你可以运用字段名称和函数来传达这样的信息,因此你无须死记它,也无须依赖注释。做法新建一个类表示数组所拥有的信息,并在其中以一个public字段保存原先数组。修改数组的所有用户,让它们改用新类的实例。编译,测试。逐一为数组元素添加取值/设值函数。根据元素的用途,为这些访问函数命原创 2021-07-18 10:40:07 · 199 阅读 · 0 评论 -
Self Encapsulate Field(自封装字段)
动机在“字段访问方式”这个问题上,存在两种截然不同的观点:其中一派认为,在该变量定义所在的类中,你可以自由访问它:另一派认为,即使在这个类中你也应该只使用访问函数间接访问。两派之间的争论可以说是如火如荼。面临选择时,我总是做两手准备。通常情况下我会很乐意按照团队中其他人的意愿来做。就我自己而言,我比较喜欢先使用直接访问方式,直到这种方式给我带来麻烦为止,此时我就会转而使用间接访问方式。重构给了我改变主意的自由。做法为待封装字段建立取值/设值函数。找出该字段的所有引用点,将它们全部改为调用取值/设原创 2021-07-16 09:38:06 · 273 阅读 · 0 评论 -
Introduce Foreign Method(引入外加函数)
动机这种事情发生过太多次了:你正在使用一个类,它真的很好,为你提供了需要的所有服务。而后,你又需要一项新服务,这个类却无法供应。于是你开始咒骂:“为什么不能做这件事?”如果可以修改源码,你便可以自行添加一个新函数:如果不能,你就得在客户端编码,补充你要的那个函数。但是不要忘记:外加函数终归是权宜之计。如果有可能,你仍然应该将这些函数搬移到它们的理想家园。如果由于代码所有权的原因使你无法做这样的搬移,就把外加函数交给服务类的拥有者,请他帮你在服务类中实现这个函数。做法在客户类中建立一个函数,用来提供原创 2021-07-15 09:40:26 · 268 阅读 · 0 评论 -
Remove Middle Man(移除中间人)
动机在Hide Delegate的“动机”一节中,我谈到了“封装受托对象”的好处。但是这层封装也是要付出代价的,它的代价就是:每当客户要使用受托类的新特性时,你就必须在服务端添加一个简单委托函数。随着受托类的特性(功能)越来越多,这一过程会让你痛苦不已。服务类完全变成了一个“中间人”,此时你就应该让客户直接调用受托类。很难说什么程度的隐藏才是合适的,还好,有了Hide Delegate和Remove Middle Man,你大可不必操心这个问题,因为你可以在系统运行过程中不断进行调整。随着系统的变化,“原创 2021-07-14 20:49:02 · 366 阅读 · 0 评论 -
Hide Delegate(隐藏“委托关系”)
动机如果某个客户先通过服务对象的字段得到另一个对象,然后调用后者的函数,那么客户就必须知晓这一层委托关系。万一委托关系发生变化,客户也得相应变化。你可以在服务对象放置一个简单的委托函数,将委托关系隐藏起来,从而去除这种依赖。这么一来,即便将来发生委托关系上的变化,变化也将被限制在服务对象中,不会涉及客户。做法对于每一个委托关系中的函数,在服务对象端建立一个简单的委托函数。调整客户,令它只调用服务对象提供的函数(如果使用者和服务提供者不在同一个包,考虑修改委托函数的访问权限,让客户得以在包之外调用它原创 2021-07-14 15:28:07 · 416 阅读 · 0 评论 -
Inline Class(将类内敛化)
动机正好与Extract Class相反。如果一个类不再承担足够责任、不再有单独存在的理由(这通常时因为此前的重构动作移走了这个类的责任),我就会挑选这一“萎缩类”的最频繁用户(也是个类),以Inline Class手法将“萎缩类”塞进另一类中。做法在目标类身上声明源类的pulic协议,并将其中所有函数委托至源类(如果“以一个独立接口表示源类函数”更合适的话,就应该在内敛之前先使用Extract Interface)。修改所有源类引用点,改而引用目标类(将源类声明为private,以斩断包之外的所原创 2021-07-12 12:11:38 · 591 阅读 · 0 评论 -
Extract Class(提炼类)
动机你也许听过类似这样的教诲:一个类应该是一个清楚的抽象,处理一些明确的责任。但是在实际工作中,类会不断成长扩展。你会在这儿加入一些功能,在那儿加入一些数据。给某个类添加一项新责任时,你会觉得不值得为这项责任分离出一个单独的类。于是,随着责任不断增加,这个类会变得过分复杂。很快,你的类就会变成一团乱麻。做法决定如何分解类所负的责任。建立一个新类,用以表现从旧类中分离出来的责任。建立“从旧类访问新类”的连接关系(有可能需要一个双向连接。但是在真正需要它之前,不要建立“从新类通往旧类”的连接)。对原创 2021-07-12 08:55:43 · 504 阅读 · 0 评论 -
Move Field(搬移字段)
动机在类之间移动状态和行为,是重构过程中必不可少的措施。随着系统的发展,你会发现自己需要新的类,并需要将现有的工作责任拖到新的类中。在这个星期看似合理而正确的设计决策,到了下个星期可能不再正确。这没问题。如果你从来没遇到这种情况,那才有问题。如果我发现对于一个字段,在其所驻类之外的另一个类中有更多函数使用了它,我就会考虑搬移这个字段。上述所谓“使用”可能是通过设值/取值函数间接进行的。我也可能移动该字段的用户(某个函数),这取决于是否需要保持接口不受变化。如果这些函数看上去很适合待在原地,我就选择搬移字原创 2021-07-10 10:26:07 · 381 阅读 · 0 评论 -
Move Method(搬移函数)
动机“搬移函数”是重构理论的支柱。如果一个类有太多行为,或如果一个类与另一个类有太多合作而形成高度耦合,我就会搬移函数。通过这样的手段,可以使系统中的类更简单,这些类最后也将更干净利落地实现系统交付的任务。做法检查源类中被源函数所使用的一切特性(包括字段和函数),考虑它们是否也该搬移(如果某个特性只被你打算搬移的那个函数用到,就应该将它一并搬移。如果另有其他函数使用了这个特性,你可以考虑将使用该特性的所有函数一起搬移。有时候,搬移一组函数比逐一搬移简单些)。检查源类的子类和超类,看看是否有该函数的原创 2021-07-09 18:05:33 · 1574 阅读 · 0 评论 -
Subsitute Algorithm(替换算法)
动机解决问题有好几种方法,我敢打赌其中某些方法会比另一些简单。算法也是如此。如果你发现做一个事可以有更清晰的方式,就应该以较清晰的方式取代复杂的方式。”重构“可以把一些复杂东西分解为较简单的小块,但有时你就必须壮士割腕,删掉整个算法,代之以较简单的算法。随着对问题有了更多理解,你往往会发现,在原先的做法之外,有更简单的解决方案,此时你就需要改变原先的算法。如果你开始使用程序库,而其中提供的某些功能/特性与你自己的代码重复,那么你也需要改变原先的算法。做法准备好另一个(替换用)算法,让它通过编译。针原创 2021-07-06 08:55:11 · 248 阅读 · 0 评论 -
Replace Method with Method Object(以函数对象去掉函数)
动机小型函数优美动人。只要将相对独立的代码从大型函数中提炼出来,就可以大大提高代码的可读性。但是,局部变量的存在会增加函数分解难度。Replace Temp with Query可以帮助你减轻这一负担,但有时候你会发现根本无法拆解一个需要拆解的函数。这种情况下,你应该把手伸进工具箱的深处。祭出函数对象这件法宝。做法建立一个新类,根据待处理函数的用途,为这个类命名。在新类中建立一个final字段,用以保存原先大型函数所在的对象。我们将这个字段称为“源对象”。同时,针对原函数的每一个临时变量和每一个参原创 2021-07-05 22:43:03 · 221 阅读 · 0 评论 -
Remove Assignments to Parameters(移除对参数的赋值)
动机首先,我要确定大家都清楚“对参数赋值”这个说法的意思。如果你把一个名为foo的对象作为参数传给某个函数,那么“对参数赋值”意味改变foo,使它引用另一个对象。如果你在“被传入对象”身上进行什么操作,那没问题,我也总是这么干。我只针对“foo被改而指向另一个对象”这种情况来讨论void aMethod(Object foo) { foo.modifyInSomeWay(); // that is OK foo = anotherObject; //trouble and despair will原创 2021-07-04 09:23:54 · 456 阅读 · 0 评论 -
Split Temporary Variable(分解临时变量)
动机临时变量有各种不同用途,其中某些用途会很自然地导致临时变量被多次赋值。“循环变量”和“结果收集变量”就是两个典型例子:循环变量会随循环的每次运行而改变;结果收集变量负责将“通过整个函数的运算”而构成的某一个值收集起来。除了这两种情况,还有很多临时变量用于保存一段冗长代码的运算结果,以便稍后使用。这种临时变量应该只被赋值一次。如果它们被赋值超过一次,就意味着它们在函数中承担了一个以上的责任。如果临时变量承担多个责任,它就应该被替换(分解)为多个临时变量,每个变量只承担一个责任。同一个临时变量承担两件不同原创 2021-07-03 10:23:04 · 349 阅读 · 0 评论 -
Introduce Explaining Variable(引入解释性变量)
动机表达式有可能非常复杂而难以阅读。这种情况下,临时变量可以帮助你将表达式分解为比较容易管理的形式。在条件逻辑中,该方法特别有价值:你可以用这项重构将每个子句提炼出来,以一个良好命名的临时变量来解释对应条件子句的意义。使用这项重构的另一种情况是,在较长算法中,可以运用临时变量来解释每一步运算的意义。做法声明一个final临时变量,将待分解之复杂表达式中的一部分动作的运算结果赋值给它。将表达式中的“运算结果”这一部分,替换为上述临时变量(如果被替换的这一部分在代码中重复出现,你可以每次一个,逐一替换原创 2021-07-01 21:32:01 · 198 阅读 · 0 评论 -
Replace Temp with Query(以查询取代临时变量)
动机临时变量的问题在于:它们是暂时的,而且只能在所属函数内使用。由于临时变量只在所属函数内可见,所以它们会驱使你写出更长的函数,因为只有这样你才能访问到需要的临时变量。如果把临时变量替换为一个查询,那么同一个类中的所有函数都将可以获得这份信息。这将带给你极大帮助,使你能够为这个类编写更清晰的代码。做法找出只被赋值一次的临时变量(如果某个临时变量被赋值超过一次,考虑使用Split Temporary Variable,将它分割成多个变量)。将该临时变量声明为final。编译(这可确保该临时变量的确原创 2021-06-28 10:32:18 · 731 阅读 · 1 评论 -
Inline Temp(内敛临时变量)
动机该方法多半是作为Replace Temp with Query的一部分使用的,所以真正的动机出现在后者那儿。唯一单独使用该方法的情况是:你发现某个变量被赋予某个函数调用的返回值。一般来说,这样的临时变量不会有任何危害,可以放心地把它留在那儿。但如果这个临时变量妨碍了其他的重构手法,例如Extract Method,你就应该把它内敛化。做法检查给临时变量赋值的语句,确保等号右边的表达式没有副作用。如果这个临时变量并未声明为final,那就将它声明为final,然后编译(这可以检查该临时变量是否真原创 2021-06-27 17:16:11 · 351 阅读 · 0 评论 -
Inline Method(内敛函数)
动机有时候你会遇到某些函数,其内部代码和函数名称同样清晰可读。也可能你重构了该函数,使其内容和其名称变得同样清晰。果真如此,你就应该去掉这个函数,直接使用其中的代码。间接性可能带来帮助,但非必要的间接性总让人不舒服做法检查函数,确定它不具有多态性(如果子类继承了这个函数,就不要将此函数内敛,因为子类无法覆写一个根本不存在的函数)。找出找个函数的所有被调用点。将找个函数的所有被调用点都替换为函数本体。编译,测试。删除该函数的定义。范例int getRating() { return (原创 2021-06-27 12:32:54 · 444 阅读 · 0 评论 -
Extract Method(提炼函数)
动机当我看到一个过长的函数或者一段需要注释才能让人理解用途的代码,我就会将这段代码放进一个独立函数中。做法创造一个新函数,根据这个函数的意图来对它命名(以它“做什么”来命名,而不是以它“怎么做”命名)。PS:即使你想要提炼的代码非常简单,例如只是一条消息或一个函数调用,只要新函数的名称能够以更好的方式昭示代码意图,你也应该提炼它。但如果你想不出一个更有意义的名称,就别动。将提炼出的代码从源函数复制到新建的目标函数中。仔细检查提炼出的代码,看看其中是否引用了“作用域限于源函数”的变量(包括局部变量原创 2021-06-25 16:19:58 · 1550 阅读 · 0 评论