程序员

设计模式系列之九模板方法模式

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

  有一个泡茶和冲咖啡的业务,他们都有差不多相似的流程。

一、不使用设计模式

public class Tea {
    //泡茶流程
    void process() {
        boilWater();
        steepTeaBag();
        pourInCup();
        addLemon();
    }

    public void boilWater() {
        System.out.println("烧水");
    }

    public void steepTeaBag() {
        System.out.println("泡茶");
    }

    public void pourInCup() {
        System.out.println("倒进杯子");
    }

    public void addLemon() {
        System.out.println("加柠檬");
    }
}
public class Coffee {
    //冲咖啡流程
    void process() {
        boilWater();
        brewCoffeeGrinds();
        pourInCup();
        addSugarAndMilk();
    }

    public void boilWater() {
        System.out.println("烧水");
    }

    public void brewCoffeeGrinds() {
        System.out.println("冲咖啡");
    }

    public void pourInCup() {
        System.out.println("倒进杯子");
    }

    public void addSugarAndMilk() {
        System.out.println("加牛奶和糖");
    }
}

二、模板方法模式的使用

  下面以一个代码样例来说明模板方法模式的使用。

//定义模板方法
public abstract class TemplateMethod {
    //冲咖啡和泡茶的模板,注意这里的final,意味着该方法不可修改,即模板中方法的先后顺序被固定
    final void template() {
        //烧水
        boilWater();
        //冲泡:用沸水"浸泡"茶叶/用沸水"冲泡"咖啡
        brew();
        //把泡好的饮料倒进杯子
        pourInCup();
        //加调料
        addCondiments();
    }
    /**这两个方法交给子类去实现**/
    //茶应该"浸泡",咖啡应该"冲泡"
    abstract void brew();
    //茶加柠檬,咖啡加牛奶和糖
    abstract void addCondiments();

    /**这两个方法属于公用方法**/
    void boilWater() {
        System.out.println("烧水");
    }
    void pourInCup() {
        System.out.println("倒进杯子");
    }
}
public class Tea extends TemplateMethod {
    public void brew() {
        System.out.println("浸泡");
    }
    public void addCondiments() {
        System.out.println("加柠檬");
    }
}
public class Coffee extends TemplateMethod {
    public void brew() {
        System.out.println("冲泡");
    }
    public void addCondiments() {
        System.out.println("加糖和牛奶");
    }
}
public class Test {
    public static void main(String[] args) {
        Tea tea = new Tea();
        Coffee coffee = new Coffee();
        //泡茶
        tea.template();
        //冲咖啡
        coffee.template();
    }
}

三、使用钩子(hook)来做一些其它操作

//定义模板方法
public abstract class TemplateMethodWithHook {
    //冲咖啡和泡茶的模板,注意这里的final,意味着该方法不可修改,即模板中方法的先后顺序被固定
    final void template() {
        //烧水
        boilWater();
        //冲泡:用沸水"浸泡"茶叶/用沸水"冲泡"咖啡
        brew();
        //把泡好的饮料倒进杯子
        pourInCup();
        //加调料
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }
    /**这两个方法交给子类去实现**/
    //茶应该"浸泡",咖啡应该"冲泡"
    abstract void brew();
    //茶加柠檬,咖啡加牛奶和糖
    abstract void addCondiments();

    /**这两个方法属于公用方法**/
    void boilWater() {
        System.out.println("烧水");
    }
    void pourInCup() {
        System.out.println("倒进杯子");
    }

    //这里是个钩子,子类可决定是否需要加调料。也就是说子类可通过这个hook控制模板定义的逻辑
    boolean customerWantsCondiments() {
        return true;
    }
}
public class TeaWithHook extends TemplateMethod {

    public void brew() {
        System.out.println("浸泡");
    }
    public void addCondiments() {
        System.out.println("加柠檬");
    }

    public boolean customerWantsCondiments() {
        //不需要加柠檬
        return false;
    }
}
public class CoffeeWithHook extends TemplateMethod {

    public void brew() {
        System.out.println("冲泡");
    }
    public void addCondiments() {
        System.out.println("加糖和牛奶");
    }

    public boolean customerWantsCondiments() {
        //需要加糖和牛奶
        return true;
    }
}
public class TestHook {
    public static void main(String[] args) {
        TeaWithHook teaHook = new TeaWithHook();
        CoffeeWithHook coffeeHook = new CoffeeWithHook();
        //泡茶,不加柠檬
        teaHook.template();
        //冲咖啡,加糖和牛奶
        coffeeHook.template();
    }
}

四、比较

不使用设计模式 使用模板方法模式
Coffee和Tea主导一切;它们控制了算法 TemplateMethod控制了一切,它拥有算法,并且保护这个算法
Coffee和Tea之间存在着重复的代码 通过TemplateMethod类实现了代码的复用
如果算法变了,需要修改Coffee和Tea类 新增算法或调整顺序只需要修改TemplateMethod类
由于类的组织不具有弹性,新加入第三种类型时需要完全重写一份 由于有模板存在,只需要实现差异化的逻辑即可
算法的知识和它的实现会分散在许多类中 TemplateMethod类专注在算法本身,而由子类提供完整的实现

上一篇:设计模式系列之八外观模式