模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

目录

1. 问题引入;

2. 第一版解决问题;

3. 第二版解决问题;

4. 模板方法初窥;

5. 定义模板方法;

6. 好莱坞原则;

7. 实际中的模板方法;

8. 总结。

1. 问题引入

咖啡和茶问题

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

我们扮演“师傅”,写一些代码来创建咖啡和茶:

咖啡

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
2. 第一版解决问题;

上面两种类的实现,出现了重复代码,我们重新编写一种解决方法。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

这种方法解决了一部分问题,但是我们是不是忽略了某些共同点呢?

3. 第二版解决问题;

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

我们可以将prepareRecipe()也抽象化,这样能节省更多的代码。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

如此,我们就解决了上面的问题。

4. 模板方法初窥;

我刚刚实现的就是模板方法模式,让我们具体看看。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

所以,模板方法定义了一个算法的步骤,允许子类为一个或多个步骤提供实现。

上面的算法相较于原始的实现由很多优点:

a. 由CaffeeBeverage类主导一切,它拥有算法,而且保护这个算法;

b. 对子类来说,CaffeeBeverage类的存在可以将代码的复用最大化;

c. 算法只存在于一个地方,所以容易修改;

d. 这个模板方法提供了一个框架,可以让其他的子类饮料插进来;

e. 父类专注在算法本身,而由子类提供完整的实现。

 

5. 定义模板方法;

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

类图如下

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

再进一步

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

更进一步

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

上图中,hook是“钩子”。

钩子:一个声明在抽象类中的方法,可以为空或者提供默认实现。

作用:让子类有能力对算法的不同点进行挂钩,是否挂钩,由子类自行决定。

我们来看看一个简单的钩子实现:

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

为了使用钩子,我们在子类中覆盖它,钩子控制了咖啡因饮料是否执行某部分算法。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

问题:当创建一个模板方法时,怎么才知道什么时候该使用抽象方法,什么时候用钩子?

当子类必须提供某个方法或步骤的实现时,就使用抽象方法。如果算法的这个部分是可选的,就用钩子。

问题:钩子的目的?

a. 让子类实现算法中可选的部分;

b. 让子类能够有机会对模板方法中某些即将发生的步骤作出反应。

问题:似乎抽象方法数目越少越少,否则在子类中实现这些方法将会很麻烦?

对的,我们应该让算法步骤不要切割得太细,也不要切割得太粗,会比较没有弹性。也请记住,某些步骤是可选的,可以将这些步骤实现成钩子,而不是实现抽象方法,可以让抽象类的子类的负荷减轻。

6. 好莱坞原则;

设计原则:别调用我们,我们会调用你。

问题

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

在好莱坞原则下,我们会允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些底层组件,换句话说,高层组件对底层组件说:别调用我们,我们会调用你。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

看看刚才的模板方法:

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

 

7. 实际中的模板方法;

我们看看排序函数中的模板方法

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

看见了吗,mergeSort就是一个模板方法,元素实现compareTo方法就可以排序了

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

问题:这真的是一个模板方法吗,还是你的想象力太丰富了?

这个模式的特点在于提供一个算法,并让子类实现某些步骤而数组的排序算法很明显的并非如此!但是我们知道荒野模式,并非总是如教科书例子一般的中规中矩,为了符合当前的环境和实现的约束,它们总要被适当的修改。这个Array类sort方法设计者受到一些约束,通常我们不能设计一个类,继承java数组,而sort方法希望能适用于所有的数组,所以它们定义了一个静态方法,而由被排序的对象的元素自行提供比较大小的算法部分,虽然不是教科书上的模板方法,但是实现时符合模板精神的,再者,由于不需要继承就可以使用这个算法,这样使得排序更有弹性,更有用。

问题:排序的实现实际上看起来更像是策略模式,而不是模板方法???

策略模式使用对象组合,我们这里正好使用数组对象排序我们的数组,这部分和策略模式非常相似,但是记住,策略模式中,组合的类实现了整个算法,而数组所实现的排序算法并不完整,它需要一个类填补compareto方法的实现,因此更像模板方法。

另外两种例子:

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

8. 总结。

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。