【重构.改善既有代码的设计】10、使接口变得更简洁易用

 

 
 

10、使接口变得更简洁易用

 

Rename Method(重新命名函数)

就是重命名而已。

 

Add Parameter(添加参数)

就是给函数添加一个参数。

但这个并不推荐,除非非得加一个参数,如果可以用其他方式,优先用其他方式。

 

Remove Parameter(移除参数)

如果一个参数没用,请移除。除非是多态的其他继承者有用到。

 

Separate Query from Modifier(将查询函数和修改函数分离)

单一职责。往往也没人这么编程吧。

 

Parameterize Method(令函数携带参数)

你可能会发现这样的两个函数:它们做着类似的工作,但因少数几个值致使动作略有不同。 
你可以将这些各自分离的函数替换为一个统一函数,并通过参数来处理那些变化情况,以简化问题。 
这样的修改可以去除重复的代码,并提高灵活性,因为你可以用这个参数处理其他(更多种)变化情况。

 

Replace Parameter with Explicit Methods(以明确函数取代参数)

相反于Parameterize Method。 
Parameterize Method是指多个函数只有参数不一样,那就该合并, 
Replace Parameter with Explicit Methods是指单个函数的多个分支完全不一样,只有入参类型一样。那就该拆开。

 

Preserve Whole Object(保持对象完整)

有时候,你会将来自同一对象的若干项数据作为参数,传递给某个函数。这样做的问题在于:万一将来被调用函数需要新的数据项,你就必须查找并修改对此函数的所有调用。如果你把这些数据所属的整个对象传给函数,可以避免这种尴尬的处境, 因为被调用函数可以向那个参数对象请求任何它想要的信息。

好处:改动的时候更容易。依赖更稳定。

 

Replace Parameter with Method(以函数取代参数)

如果函数可以通过其他途径(而非参数列〕获得参数值,那么它就不应该通过参数取得该值。过长的参数列会增加程序阅读者的理解难度,因此我们应该尽可能缩短参数列的长度。

 

Introduce Parameter Object(引入参数对象)

你常会看到特定的一组参数总是一起被传递。可能有好几个函数都使用这一组参数,这些函数可能隶属同一个class,也可能隶属不同的classes 。这样一组参数就是所谓的Date Clump (数据泥团)」,我们可以运用一个对象包装所有这些数据,再以该对象取代它们。哪怕只是为了把这些数据组织在一起,这样做也是值得的。本项重构的价值在于「缩短了参数列的长度」,而你知道,过长的参数列总是难以理解的。此外,新对象所定义的访问函数(accessors)还可以使代码更具一致性,这又进一步降低了代码的理解难度和修改难度。

本项重构还可以带给你更多好处。当你把这些参数组织到一起之后,往往很快可以发现一些「可被移至新建class」的行为。通常,原本使用那些参数的函数对那些参数会有一些共通措施,如果将这些共通行为移到新对象中,你可以减少很多重复代码。

 

Remove Setting Method(移除设置函数)

如果你为某个值域提供了设值函数(setter),这就暗示这个值域值可以被改变。如果你不希望在对象初创之后此值域还有机会被改变,那就不要为它提供设值函数 (同时并将该值域设为final )。这样你的意图会更加清晰,并且往往可以排除其值被修改的可能性——这种可能性往往是非常大的。

 

Hide Method(隐藏某个函数)

有一个函数,从来没有被其他任何class 用到。

将这个函数修改为private 。

 

Replace Constructor with Factory Method(以「工厂函数」取代「构造函数」)

在创建一个对象比简单构造要复杂时,使用工厂。

 

Encapsulate Downcast(封装「向下转型」动作)

就是把对返回结果的强制类型转换放在函数里面进行,返回的就是转换后的类型。

 

Replace Error Code with Exception(用异常取代错误码)

好处是在调用链很长的时候,可以直接由需要处理的地方捕捉处理,而不是一层层向上返回错误码。

 

Replace Exception with Test(以测试取代异常)

意思是要做足参数检测,避免直接使用,靠异常处理已知的错误。

 

原作者的总结

在对象技术中,最重要的概念莫过于「接口」(interface)。容易被理解和被使用的接口,是开发良好面向对象软件的关键。本章将介绍「使接口变得更简洁易用」 的重构手法。

最简单也最重要的一件事就是修改函数名称。「名称」是程序写作者与阅读者交流的关键工具。只要你能理解一段程序的功能,就应该大胆地使用Rename Method 将你所知道的东西传达给其他人。

函数参数在「接口」之中扮演十分重要的角色。 Add Parameter 和Remove Parameter 都是很常见的重构手法。 
初始接触面向对象技术的程序员往往使用很长的参数列(parameter lists),这在其他开发环境中是很典型的方式。 
但是, 使用对象技术,你可以保持参数列的简短,以下有一些相关的重构可以帮助你缩短参数列。 
如果来自同一对象的数个值被当作参数传递,你可以运用 Preserve Whole Object 将它们替换为单一对象,从而缩短参数列。 
如果此前并不存在这样一个对象,你可以运用Introduce Parameter Object将它创建出来。 
如果函数参数来自该函数可取用的一个对象,则可以使用 Replace Parameter with Method 避免传递参数。 
如果某些参数被用来在条件式中做选择依据,你可以实施 Replace Parameter with Explicit Methods。 
另外,你还可以使用Parameterize Method 为数个相似函数添加参数,将它们合并到一起。

关于缩减参数列的重构手法,Doug Lea 对我提出了一个警告:并发编程(con-current programming)往往需要使用较长的参数列,因为这样你可以保证传递给函数的参数都是不可被修改的,就像内置型对象和value object 一定地不可变。 
通常,你可以使用不可变对象(immutable object)取代这样的长参数列,但另一方面你也必须对此类重构保持谨慎。

多年来我一直坚守一个很有价值的习惯:明确地将「修改对象状态」的函数(修改函数,modifiers)和「查询对象状态」的函数(查询函数,queries)分开设计。 
不知道多少次,我因为将这两种函数混在一起而麻烦缠身;不知道多少次,我看到别 人也因为同样的原因而遇到同样的麻烦。 
因此,如果我看到这两种函数混在一起, 我就使用 Separate Query from Modifier 将它们分开。

良好的接口只向用户展现必须展现的东西。如果一个接口暴露了过多细节,你可以将不必要暴露的东西隐藏起来,从而改进接口的质量。 
毫无疑问,所有数据都应该隐藏起来(希望你不需要我来告诉你这一点),同时,所有可以隐藏的函数都应该被隐藏起来。进行重构时,你往往需要暂时暴露某些东西,最后再以 Hide Method 和Remove Setting Method 将它们隐藏起来。

构造函数(constructors)是Java 和C++ 中特别麻烦的一个东西,因为它强迫你必须知道「待建对象」属于哪一个class ,而你往往并不需要知道这一点。你可以使用Replace Constructor with Factory Method 避免了解这「被迫了解的一点」。

转型(casting)是Java 程序员心中另一处永远的痛。你应该尽量使用Encapsulate Downcast 将「向下转型动作」封装隐藏起来,避免让class 用户做那种动作。

和许多现代编程语言一样,Java 也有异常处理(exception-handing)机制,这使得错误处理(error handling)相对容易一些。不习惯使用异常的程序员,往往会以错误代码(error code)表示程序遇到的麻烦。你可以使用Replace Error Code with Exception 来运用这些崭新的异常特性。但有时候异常也并不是最合适的选择,你应该实施Replace Exception with Test 先测试一番。

 

我的总结

1、函数名:必须表意。 
2、参数的几个原则: 
2.1、尽可能不添加参数,除非必须。 
2.2、移除不再使用的参数。 
2.3、按整个对象传参,而不是取其中的几个成员。 
2.4、如果一组参数总是被传递,创建一个类包含它们,然后把这个类作为参数传递。 
2.5、如果函数能自己获取的内容,就不要作为参数。 
3、函数Or参数: 
3.1、多个函数功能和实现类似,只有参数不同,则合并,用参数区分。 
3.2、单个函数按参数区分的不同分支完全不同,则拆分为独立的多个函数。 
4、隐藏没其他类调用的函数。 
5、返回值:在函数内部完成类型转换。 
7、异常: 
7.1、用异常替代返回码 
7.2、异常不能替代必要的检测。

 

 
 
 
 
posted @ 2019-02-15 23:49  傲衣华少  阅读(502)  评论(0编辑  收藏  举报