设计模式之模板方法模式

定义

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

UML类图

这里写图片描述

这里涉及到两个角色:抽象模板(Abstract Template)具体模板(Concrete Template)。

抽象模板有如下责任:

  • 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。
  • 定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。

具体模板有如下责任:

  • 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。
  • 每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。

实例

咖啡店卖咖啡和茶两种饮料,两种饮料的冲泡方法分别为。

咖啡冲泡法:
1:把水煮沸
2:用沸水冲泡咖啡
3:把咖啡倒进杯子
4:加糖和牛奶

茶冲泡法
1:把水煮沸
2:用沸水浸泡茶叶
3:把茶倒进杯子
4:加柠檬

显而易见,可以将其中独特的方法2和4,抽离出来,在子类中实现。

具体看代码:
抽象类CaffeineBeverage

//模板方法必须定义成抽象类
public abstract class CaffeineBeverage {

    final void prepareRecipe() {
        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");
    }
}

Coffee类:

public class Coffee extends CaffeineBeverage {
    public void brew() {
        System.out.println("Dripping Coffee through filter");
    }
    public void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }
}

这里只实现了父类CaffeineBeverage的抽象方法,即Coffee类独特的方法,其余共有的方法交给父类实现。

Tea

public class Tea extends CaffeineBeverage {
    public void brew() {
        System.out.println("Steeping the tea");
    }
    public void addCondiments() {
        System.out.println("Adding Lemon");
    }
}

测试类:

public class BeverageTestDrive {
    public static void main(String[] args) {

        Tea tea = new Tea();
        Coffee coffee = new Coffee();

        System.out.println("\nMaking tea...");
        tea.prepareRecipe();

        System.out.println("\nMaking coffee...");
        coffee.prepareRecipe();

    }
}

结果如下

//结果
Making tea...
Boiling water
Steeping the tea
Pouring into cup
Adding Lemon

Making coffee...
Boiling water
Dripping Coffee through filter
Pouring into cup
Adding Sugar and Milk

这个就是模板方法模式的简单实现,如你所见,模板就是一个方法,更具体的说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这样可以确保算法结构保持不变,同时由子类提供部分实现。

更深入的模板方法模式

我们可以新一个方法,使用钩子,用来控制饮料是否执行某部分算法,即是否需要增加调料。

public abstract class CaffeineBeverageWithHook {

    void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    abstract void brew();

    abstract void addCondiments();

    void boilWater() {
        System.out.println("Boiling water");
    }

    void pourInCup() {
        System.out.println("Pouring into cup");
    }

    //钩子类
    boolean customerWantsCondiments() {
        return true;
    }
}

改造后的tea

public class CoffeeWithHook extends CaffeineBeverageWithHook {

    public void brew() {
        System.out.println("Dripping Coffee through filter");
    }

    public void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }

    public boolean customerWantsCondiments() {

        String answer = getUserInput();

        if (answer.toLowerCase().startsWith("y")) {
            return true;
        } else {
            return false;
        }
    }

    private String getUserInput() {
        String answer = null;

        System.out.print("Would you like milk and sugar with your coffee (y/n)? ");

        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        try {
            answer = in.readLine();
        } catch (IOException ioe) {
            System.err.println("IO error trying to read your answer");
        }
        if (answer == null) {
            return "no";
        }
        return answer;
    }
}

测试类:

public class BeverageTestDrive {
    public static void main(String[] args) {

        TeaWithHook teaHook = new TeaWithHook();

        System.out.println("\nMaking tea...");
        teaHook.prepareRecipe();

    }
}

结果

Making tea...
Boiling water
Steeping the tea
Pouring into cup
Would you like lemon with your tea (y/n)? y
Adding Lemon

这样,我们就实现了,钩子作为条件控制,影响抽象类的算法流程。钩子除了作为条件控制之外,还可以作为抽象类的某些可选的方法,这样可以让子类去决定是否需要执行父类的某些方法。

总结

模板方法中的方法可以分为两大类:模板方法基本方法

模板方法

一个模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。如上面例子中的prepareRecipe()方法。

一个抽象类可以有任意多个模板方法,而不限于一个。每一个模板方法都可以调用任意多个具体方法。

基本方法

基本方法又可以分为三种:抽象方法(Abstract Method)具体方法(Concrete Method)钩子方法(Hook Method)。

  • 抽象方法: 一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示。
  • 具体方法: 一个具体方法由抽象类声明并实现,而子类并不实现或置换。
  • 钩子方法: 一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空实现,作为方法的默认实现。

在前面的UML示例中,AbstractClass是一个抽象类,它带有三个方法。其中abstractMethod()是一个抽象方法,它由抽象类声明为抽象方法,并由子类实现;hookMethod()是一个钩子方法,它由抽象类声明并提供默认实现,并且由子类置换掉。concreteMethod()是一个具体方法,它由抽象类声明并实现。

提示:钩子方法的名字应当以do开始。

Java中的实例

Java的集合就是一个典型的,利用了模板方法模式的例子。Java集合中的Collection集合包括List和Set两大组成部分。
List是队列,而Set是没有重复元素的集合。它们共同的接口都在Collection接口声明;例如,都包含了size()isEmpty()方法。
而AbstractCollection这个抽象类则实现了它们共同的方法,其余未实现的方法定义为抽象方法。List和Set的实例类,就是通过继承AbstractCollection(或它的子类),省去了许多重复性编码的工作!

设计模式原则

通过模板方法模式,我们可以学习到一个新的设计原则:好莱坞原则。

好莱坞原则:别调用我们,我们会调用你。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值