Switch branches/tags
Nothing to show
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
..
Failed to load latest commit information.
src/main/java/me/lishuo/templatemethod
README.md
pom.xml

README.md

JAVA设计模式系列:

模板方法模式

定义

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

实现

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

首先我们定义一个抽象类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类,重写了钩子,来控制加料的步骤。

如有纰漏,烦请指出。

参考《Head First 设计模式》