一、概念
- 模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
- 解析:模板方法模式用来创建一个算法的模板。什么是模板?模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这可以确保算法的结构保持不变,同时由子类提供部分实现。
- 角色: 1、抽象模板(Abstract Template):定义模板方法,规范了一组步骤,其中父类可以实现其中的某些步骤,需要由子类实现的步骤声明为 abstract。 2、具体模板(Concrete Template):继承抽象父类,实现抽象父类中声明为 abstract 的方法。
二、Demo 实现
我们打算定义一个咖啡因饮料冲泡流程,把流程中相同的步骤放在一起,同时,不同的饮料还能有自己的具体实现。
1、抽象模板
public abstract class CaffeineBeverage { /** * @Description * 1、模板方法,定义算法的步骤。 * 2、我们一般把模板方法定义成 final,不希望子类覆盖。 */ public final void prepareRecipe() { //1、把水煮沸 boilWater(); //2、用热水泡饮料(具体什么饮料未知) brew(); //3、把饮料倒进杯子 pourIncup(); //4、往饮料中添加调料(具体什么调料未知) if (wantAddCondiments()) { addCondiments(); } } /** * @Description 需要子类提供实现的步骤定义为抽象 */ protected abstract void brew(); protected abstract void addCondiments(); /** * @Description 由父类提供实现,又不希望被子类重写的,封装成 private。 */ private void boilWater() { System.out.println("Boiling water"); } private void pourIncup() { System.out.println("Pouring into cup"); } /** * @Description 钩子方法 * 1、钩子是一种被声明在抽象类中的方法,当只有空的或者默认的实现。 * 2、钩子的存在,可以让子类有能力对算法的不同点进行挂钩(重写)。 * 3、要不要挂钩(重写),由子类自己决定。 */ protected boolean wantAddCondiments() { return true; }}
这里,我们定义了一个抽象模板,规范了具体的冲泡流程 —— 把水煮沸->泡饮料->倒杯子->添加调料。在抽象模板中,我们有三种类型的方法,一种需要子类提供实现的,我们定义为 abstract ;一种父类提供实现,又不希望被子类覆盖的,我们定义为 private;还有一种称为钩子方法,父类提供默认实现,子类可自由选择是否重写父类的方法。
2、具体模板
public class Coffee extends CaffeineBeverage { @Override protected void brew() { System.out.println("Dripping Coffee through filter"); } @Override protected void addCondiments() { System.out.println("Adding Suger and Milk"); }}
public class Tea extends CaffeineBeverage { @Override protected void brew() { System.out.println("Steeping the tea"); } @Override protected void addCondiments() { System.out.println("Adding Lemon"); } /** * @Description 子类重写钩子方法 */ @Override protected boolean wantAddCondiments() { return false; }}
具体模板继承了抽象模板,实现了抽象模板中定义为 abstract 的步骤方法,并可以自己选择是否重写钩子方法。
3、测试
public class Test { public static void main(String[] args) { CaffeineBeverage coffee = new Coffee(); CaffeineBeverage tea = new Tea(); coffee.prepareRecipe(); tea.prepareRecipe(); }}
三、总结
- 模板方法模式的通用类图非常简单,仅仅使用了Java的继承机制,但它是一个非常广泛的模式。
- 优点: 1、封装不变部分,扩展可变部分。把认为不变部分的算法封装到父类中实现,而可变部分的则可以通过继承来继续扩展。可以将代码最大复用化。 2、父类规范算法行为,子类提供完整实现。
- 使用场景: 1、多个子类有共有的方法,并且逻辑基本相同。 2、一次性实现一个算法的不变部分,并且将可变的行为留给子类来完成。
- 为防止子类改变模板方法中的算法,可以将模板方法声明为 final。
- 策略模式和模板方法模式都封装算法,但是策略模式使用的是组合,模板方法模式使用的是继承。
- 工厂方法是模板方法的一种特殊版本。
- 模板方法导致一种反向的控制结构,这种结构有时被称为“好莱坞原则“。
- 好莱坞原则:别调用我们,我们会调用你。在好莱坞原则之下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式是“别调用我们,我们会调用你”。
- 低层组件可以调用高层组件中的方法(实际上子类会常常调用其从父类中继承所来的方法),但是我们要做的是要避免让高层和底层组件之间有明显的环状依赖。