美文网首页程序员
设计模式系列-模板方法模式

设计模式系列-模板方法模式

作者: 特立独行的猪手 | 来源:发表于2017-03-18 18:56 被阅读120次

    JAVA设计模式系列:

    模板方法模式

    定义

    模板方法模式在一个方法中定义了算法的骨架,把其中的某些步骤延迟到子类的实现,是为我们提供了代码复用的一种重要的技巧。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

    实现

    这里简单通过一个示例来展示到底什么时候模板方法模式。这个示例向我们展示了制作咖啡和茶2种咖啡因饮料的过程,在这个过程中展示了模板方法模式的具体使用方法。
    代码地址:GitHub
    先看一下模板方法模式的类图:


    首先我们定义一个抽象类CaffeineBeverage来作为模板方法的基类。具体代码如下:
    public abstract class CaffeineBeverage {
    // 模板方法
    final void prepareReipe() {
    boilWater();
    brew();
    pourInCup();
    addCondiments();
    }
    // 浸泡
    abstract void brew();
    // 加料
    abstract void addCondiments();
    // 煮水
    void boilWater() {
    System.out.println("Boiling water");
    }
    // 倒进杯子里
    void pourInCup() {
    System.out.println("Pouring into cup");
    }
    

    CaffeineBeverage类中定义了一个名为prepareReipe()的模板方法,用来描述冲泡咖啡因饮料的过程。方法用final修饰是为了防止子类修改方法的执行顺序。
    CaffeineBeverage类定义了4个方法,分别是brew()addCondiments()boilWater()pourInCup()。在我们的示例中,冲泡咖啡和茶共有的过程分别是煮水 boilWater()倒进杯子里 pourInCup()。这两个共用方法选择在CaffeineBeverage类实现。
    Tea类、Coffee类是CaffeineBeverage类的子类。而加料 addCondiments()浸泡 brew()分别在Tea类、Coffee类中有各自不同的实现。如下所示:

    public class Tea extends CaffeineBeverage {
    void brew() {
    System.out.println("Stepping the tea.");
    }
    void addCondiments() {
    System.out.println("Adding Lemon");
    }
    }
    
    public class Coffee extends CaffeineBeverage {
    void brew() {
    System.out.println("Dripping Coffee through filter");
    }
    void addCondiments() {
    System.out.println("Adding Suger and Mike");
    }
    }
    

    完成了模板方法模式的代码,我们可以进行测试一下,测试类:

    public class Test {
    public static void main(String[] args) {
    Tea tea = new Tea();
    tea.prepareReipe();
    System.out.println("**************");
    Coffee coffee = new Coffee();
    coffee.prepareReipe();
    }
    }
    

    输出结果:

    Boiling water
    Stepping the tea.
    Pouring into cup
    Adding Lemon
    **************
    Boiling water
    Dripping Coffee through filter
    Pouring into cup
    Adding Suger and Mike
    

    我们将冲茶和咖啡重复的方法煮水 boilWater()倒进杯子里 pourInCup()抽象出来,每个子类分别去实现各自特有的步骤。以上便是模板方法的实例。

    钩子

    还需了解到,模板方法模式还有钩子的概念。钩子是一种被声明在抽象类的方法,可以为空或者默认的实现。钩子的存在可以让子类有能力对算法的不同点进行挂钩,是否需要挂钩由子类决定。
    借助上面的示例来展示钩子如何使用。首先我们在抽象类CaffeineBeverage定一个钩子,钩子的默认实现返回true。如下:

    // 定义一个钩子
    boolean customerWantsCondiments() {
    return true;
    }
    

    并修改模板方法:

    // 模板方法
    final void prepareReipe() {
    boilWater();
    brew();
    pourInCup();
    if (customerWantsCondiments()) {
    addCondiments();
    }
    }
    

    目的是增加让客户选择是否需要给茶或者饮料来添加东西。我们可以在子类中覆盖钩子的写法。这里改下下Tea类,如下:

    public class Tea extends CaffeineBeverage {
    private String msg;
    public Tea(String msg) {
    this.msg = msg;
    }
    void brew() {
    System.out.println("Stepping the tea.");
    }
    void addCondiments() {
    System.out.println("Adding Lemon");
    }
    boolean customerWantsCondiments() {
    if ("y".equals(this.msg)) {
    return true;
    } else {
    return false;
    }
    }
    }
    

    添加了一个msg变量,可以通过构造函数进行赋值,当msgy时候,我们将在茶里添加柠檬,否则不添加。看一下测试代码:

    public static void main(String[] args) {
    Tea tea = new Tea("n");
    tea.prepareReipe();
    System.out.println("**************");
    Coffee coffee = new Coffee();
    coffee.prepareReipe();
    }
    

    运行结果:

    Boiling water
    Stepping the tea.
    Pouring into cup
    **************
    Boiling water
    Dripping Coffee through filter
    Pouring into cup
    Adding Suger and Mike
    

    和上面的比较一下,发现制作茶的过程中缺少了添加东西的过程,主要是因为我们在Tea类,重写了钩子,来控制加料的步骤。

    如有纰漏,烦请指出。

    代码地址:GitHub

    参考《Head First 设计模式》

    相关文章

      网友评论

        本文标题:设计模式系列-模板方法模式

        本文链接:https://www.haomeiwen.com/subject/rwcpnttx.html