搬移特性
Tip
模块化是优秀软件设计的核心所在,好的模块化能够让我在修改程序时只需理解程序的一小部分。
搬移函数(Move Function)
title | content |
---|---|
场景 | 为了设计出高度模块化的程序,我得保证互相关联的软件要素都能集中到一块,并确保块与块之间的联系易于查找、直观易懂。 同时,我对模块设计的理解并不是一成不变的,随着我对代码的理解加深,我会知道那些软件要素如何组织最为恰当。 要将这种理解反映到代码上,就得不断地搬移这些元素。 |
定义 | 在该函数最常引用的类中建立一个有着类似行为的新函数。 将旧函数变成一个单纯的委托函数,或是将旧函数完全移除 |
意义 | 模块化是优秀软件设计的核心所在,好的模块化能够让我在修改程序时只需理解程序的一小部分。 |
做法 | 检查函数在当前上下文里引用的所有程序元素(包括变量和函数),考虑是否需要将它们一并搬移 如果发现有些被调用的函数也需要搬移,我通常会先搬移它们。这样可以保证移动一组函数时,总是从依赖最少的那个函数入手。 如果该函数拥有一些子函数,并且它是这些子函数的唯一调用者,那么你可以先将子函数内联进来,一并搬移到新家后再重新提炼出子函数。 |
检查待搬移函数是否具备多态性。 在面向对象的语言里,还需要考虑该函数是否覆写了超类的函数,或者为子类所覆写。 | |
注意 |
搬移字段(Move Field)
title | content |
---|---|
场景 | 程序中,某个字段被其所驻类之外的另一个类更多地用到。 |
定义 | 在目标类新建一个字段,修改源字段的所有用户,令它们改用新字段。 |
意义 | 内聚 |
做法 | |
注意 |
搬移语句到函数(Move Statements into Function)
title | content |
---|---|
场景 | |
意义 | |
定义 | |
做法 | 如果重复的代码段离调用目标函数的地方还有些距离,则先用【移动语句】将这些语句挪动到紧邻目标函数的位置。 如果目标函数仅被唯一一个源函数调用,那么只需将源函数中的重复代码段剪切并粘贴到目标函数中即可,然后运行测试。本做法的后续步骤至此可以忽略。 如果函数不止一个调用点,那么先选择其中一个调用点应用【提炼函数】,将待搬移的语句与目标函数一起提炼成一个新函数。给新函数取个临时的名字,只要易于搜索即可。 调整函数的其他调用点,令它们调用新提炼的函数。每次调整之后运行测试。 完成所有引用点的替换后,应用【内联函数】将目标函数内联到新函数里,并移除原目标函数。 对新函数应用【函数改名】,将其改名为原目标函数的名字。 如果你能想到更好的名字,那就用更好的那个。 |
注意 |
搬移语句到调用者(Move Statements to Callers)
Warning
作为程序员,我们的职责就是设计出结构一致、抽象合宜的程序,而程序抽象能力的源泉正是来自函数
我得思考这个问题:如果我把代码移动过去,执行次序的不同会不会使代码之间产生干扰,甚至于改变程序的可观测行为?
title | content |
---|---|
场景 | 如果有几行代码取用了同一个数据结构,那么最好是让它们在一起出现,而不是夹杂在取用其他数据结构的代码中间。 最简单的情况下,我只需使用移动语句就可以让它们聚集起来。 此外还有一种常见的"关联",就是关于变量的声明和使用。 |
意义 | |
定义 | |
做法 | |
注意 | 如果原函数是一个超类方法,并且有子类进行了覆写,那么还需要对所有子类的覆写方法进行同样的提炼操作,保证继承体系上每个类都有一份与超类相同的提炼函数。 接着将子类的提炼函数删除,让它们引用超类提炼出来的函数。 |
移动语句(Slide Statements)
title | content |
---|---|
场景 | |
意义 | 让存在关联的东西一起出现,可以使代码更容易理解。 |
定义 | |
做法 | |
注意 |
移除死代码(Remove Dead Code)
title | content |
---|---|
场景 | |
意义 | |
定义 | |
做法 | 如果死代码可以从外部直接引用,比如它是一个独立的函数时,先查找一下还有无调用点。 |
将死代码移除。 测试 | |
注意 |