引子

  • 这是一个很简单的模式,却被非常广泛的使用。
  • 之所以简单是因为在这个模式中仅仅使用到了继承关系。 继承关系由于自身的缺陷,被专家们扣上了“罪恶”的帽子。“使用委派关系代替继承关 系”,“尽量使用接口实现而不是抽象类继承”等等专家警告,让我们这些菜鸟对继承“另眼相 看”。
  • 其实,继承还是有很多自身的优点所在。只是被大家滥用的似乎缺点更加明显了。合理的利用继承关系,还是能对你的系统设计起到很好的作用的。而模板方法模式就是其中的一个使用范例。

定义与结构

  • 模板方法(Template Method)模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤

  • 这里的算法的结构,可以理解为你根据需求设计出来的业务流程。

  • 特定的步骤就是指那些可能在内容上存在变数的环节。 可以看出来,模板方法模式也是为了巧妙解决变化对系统带来的影响而设计的。

  • 使用模板方法使系统扩展性增强,最小化了变化对系统的影响。这一点,在下面的举例中可以很明显的看出来。

举例

  • JUnit 中的 TestCase 以及它的子类就是一个模板方法模式的例子。

  • 在 TestCase 这个抽象类中将整个测试的流程设置好了,比如 先执行 Setup 方法初始化测试前提,再运行测试方法,然后再 TearDown 来取消测试设置。 但是你将在 Setup、TearDown 里面作些什么呢?鬼才知道呢!!因此,而这些步骤的具体实现都延迟到子类中去,也就是你实现的测试类中。

  • 来看下相关的源代码吧。 这是 TestCase 中,执行测试的模板方法。你可以看到,里面正像前面定义中所说的那 样,它制定了“算法”的框架——先执行 setUp 方法来做下初始化,然后执行测试方法,最 后执行 tearDown 释放你得到的资源。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public void runBare() throws Throwable {
    setUp();
    try {
    runTest();
    }
    finally {
    tearDown();
    }
    }
  • 这就是上面使用的两个方法。与定义中不同的是,这两个方法并没有被实现为抽象方法, 而是两个空的无为方法(被称为钩子方法)。这是因为在测试中,我们并不是必须要让测试 程序使用这两个方法来初始化和释放资源的。

    • 如果是抽象方法,则子类们必须给它一个实现, 不管用到用不到。这显然是不合理的。
    • 使用钩子方法,则你在需要的时候,可以在子类中重写这些方法。
  • 使用模板方法模式可以将代码的公共行为提取出来,达到复用的目的。而且 在模板方法模式中,是由父类的模板方法来控制子类中的具体实现。这样你在实现子类的时 候,根本不需要对业务流程有太多的了解。