重构之简化函数调用

来源:互联网 发布:linux centos 编辑:程序博客网 时间:2024/05/19 22:27
一.函数改名
函数的名称未能揭示函数的用途,修改函数的名称。
我极力提倡的一种编程风格就是:将复杂的处理过程分解成小函数。但是,如果做得不好,这会使你费尽周折却弄不清楚这些小函数各自的用途。要避免这种麻烦,关键就在于给函数起一个好名称。函数的名称应该准确表达它的用途。给函数命名有一个好办法,首先考虑应该给这个函数协商一句怎样的注释,然后再想办法将注释变成函数名称。
生活就是如此。你常常无法第一次就给函数起一个好名称。这时候你可能会想,就这样将就着吧,毕竟只是一个名称而已。当心,这是恶魔的召唤,千万不要被它诱惑。如果你看到一个函数名称不能很好地表达它的用途,应该马上加以修改。记住,你的代码首先是为人写的,其次才是为计算机写的。而人需要良好名称的函数。如果给每个函数都起一个良好的名称,也许可以节约很多时间。
二.添加参数
某个函数需要从调用端得到更多信息,为此函数添加一个对象参数,让该对象带进函数所需信息。
添加参数是一个很常用的重构手段,使用这项重构的动机很简单,你必须修改一个函数,而修改后的函数需要一些过去没有的信息,因此你需要给该函数添加一个参数。
实际上我比较需要说明的是,不使用本重构的时机。除了添加参数外,你常常还有其他选择。只要可能,其他选择都比添加参数要好,因为他们不会增加参数列的长度。过长的参数列是不好的味道,因为程序员很难记住那么多参数,而且长参数列往往伴随着坏味道数据泥团。
请看看现有的参数,然后问自己,你能从这些参数得到所需的信息吗?如果回答是否定的,有可能通过某个函数提供所需信息吗?你究竟把这些信息用于何处?这个函数是否应该属于拥有该信息的那个对象所有?看看现有参数,考虑一下,加入新参数是否合适?
三.移除参数
程序员可能经常添加参数,却往往不愿意去掉它们。他们打的如意算盘是:无论如何,多余的参数不会引起任何问题,而且以后还可能用上它。
这也是恶魔的诱惑,一定要把它匆匆脑子里赶出去,参数代表着函数所需的信息,不同的参数值有不同的意义。函数调用者必须为每一个参数操心该传什么东西进去。如果你不去掉多余参数,就是让你的每一位用户多费一份心。是很不划算的,更何况去掉参数是非常简单的一项重构。
但是,对于多态函数,情况有所不同。这种情况下,可能多态函数的另一份实现会使用这个参数,此时你就不能去除它。你可以添加一个独立函数,在这种情况下使用,不过你应该先检查调用者如何使用这个函数,以决定是否值得这么做。如果某些调用着已经知道他们正在处理的是一个特定的子类,并且已经做了额外工作找出自己需要的参数,或已经利用对类体系的了解来避免获取到null,那么就值得你建立一个新函数,去除那多余的参数。如果调用者不需要了解该函数所属的类,你也可以继续保持调用者无知而幸福的状态。
四.将查询函数和修改函数分离
某个函数既返回对象状态值,又修改对象状态。建立两个不同的函数,其中一个负责查询,另一个负责修改。
如果某个函数只是向你提供一个值,没有任何看得到的副作用,那么这是个很有价值的东西。你可以任意调用这个函数,也可以把调用动作搬到函数的其他地方。简而言之,需要操心的事情少多了。
明确表现出"有副作用"与"无副作用"两种函数之间的差异,是个很好的想法。下面是一条好规则:任何有返回值的函数,都不应该有看得到的副作用。有些程序员甚至将次作为一条必须遵守的规则。就像对待任何东西一样,我并不绝对遵守它,不过我总是尽量遵守,而它也回报我很好的效果。
如果你遇到一个既有返回值又有副作用的函数,就应该试着将查询动作从修改动作中分割出来。
五.引入参数对象
某些参数总是很自然地同时出现,以一个对象取代这些参数。
你常会看到特定的一组参数总是一起被传递。可能有好几个函数都使用这一组参数,这些参数可能隶属同一个类,也可能隶属不同的类。这样一组参数就是所谓的数据泥团,我们可以运用一个对象包装所有这些数据,再以该对象取代它们。哪怕只是为了把这些数据组织在一起,这么做也是值得的。此外,新对象所定义的访问函数还可以是代码更具一致性,这又进一步降低了理解和修改代码的难度。
本项重构还可以带给你更多好处。当你把这些参数组织到一起之后,往往很快可以发现一些可以被移至新建类的行为。通常原本使用那些参数的函数对这一组参数会有一些共通的处理,如果将这些共通行为移到新对象中,你可以减少很多重复代码。
六.移除设值函数
类中的某个字段应该在对象创建时被设值,然后就不再改变。去掉该字段的所有设值函数。
如果你为某个字段提供了设值函数,这就暗示这个字段值可以被改变。如果你不希望在对象创建之后此字段还有机会被改变,那就不要为它提供设值函数。这样你的意图会更加清晰,并且可以排除其值被修改的可能性,这种可能性往往是非常大的。
如果你保留了间接访问变量的方法,就可能经常有程序员盲目使用它们。这些人甚至会在构造函数中使用设值函数。
七.隐藏函数
有一个函数,从来没有被其他任何类用到,将这个函数修改为private。
重构往往促使你修改函数的可见度。提高函数可见度的情况很容易想象:另一个类需要用到某个函数,因此你必须提高该函数的可见度。但是要指出一个函数的可见度是否过高,就稍微困难一些。理想情况下,你可以使用工具检查所有函数,指出可被隐藏起来的函数。即使没有这样的工具,你也应该时常进行这样的检查。
八.以工厂函数取代构造函数
你希望在创建对象时不仅仅是做简单的建构工作。将构造函数替换为工厂函数。
使用以工厂函数取代构造函数的最显而易见的动机,就是在派生子类的过程中以工厂函数取代类型码。你可能常常需要根据类型码创建相应的对象,现在,创建名单中还得加上子类,那些子类也是根据类型码来创建。然而由于构造函数只能返回单一类型的对象,因此你需要将构造函数替换为工厂函数。
九.封装向下转型
在强类型OO语言中,向下转型是最烦人的事情之一。之所以很烦人,是因为从感觉上来说它完全没有必要:你竟然越俎代庖地告诉编译器某些应该由编译器自己计算出来的东西。但是,由于计算对象类型往往比较麻烦,你还是常常需要亲自告诉编译器对象的确切类型。向下转型在Java特别盛行,因为Java没有模板机制,因此如果你想从集合之中取出一个对象,就必须进行向下转型。
向下转型也许是一种无法避免的罪恶,但你仍然应该尽可能少做。如果你的某个函数返回一个值,并且你知道所返回的对象类型比函数签名所昭告的更特化,你便是在函数用户身上强加了非必要的工作。这种情况下你不应该要求用户承担向下转型的责任,应该尽量为他们提供准确的类型。
原创粉丝点击