首页 > 技术文章 > 设计模式学习 —— 模板方法

liweiwei1419 2017-04-29 09:22 原文

设计模式学习 —— 模板方法

模板模式:封装了一个算法步骤,并允许子类为一个或者多个步骤方法提供实现。模板模式可以在子类不改变算法结构的情况下,重新定义算法中的某些步骤。

固定的东西成为模板在抽象父类中。

个性化的步骤在子类中实现。

身边的模板模式:
我们用的很多框架都是用模板来完成的:
1、排序鸭子 :Arrays.sort() 这个就是个模板方法,Ducks 这个类实现 Comparable 接口
2、自定义控件
3、

编写模板方法类

要点:1、设计成抽象类的原因是这个类中有未实现的方法,之所以不设计成接口,是因为这个类中有固定实现的方法(是不是 Java8 中可以用接口来实现,因为 Java8 有接口默认实现方法);
2、整个模板方法 prepareBeverageTemplate() 设计成 final ,体现了模板的固定性。
既然叫“模板”,意思是按照固定的模式去做一件事情,所以模板这件事情上不能留给客户端空间,否则就失去了模板的意义。
3、固定的步骤,已经实现的方法,使用 private 修饰,子类不能修改,这一部分是模板类中固定的部分;
4、须要让子类完成的步骤,应该使用 protected 修饰,延迟到子类实现;
5、关注到钩子函数,钩子函数使用 protected 修饰,并且不是抽象的方法,是一个具体的方法,子类可以根据须要去重写。

/**
 * Created by Liwei on 2016/7/4.
 * 抽象基类,为所有子类提供一个算法框架
 * 提神饮料
 */
public abstract class RefreshBeverage {

    /**
     * 制备饮料的模板方法
     * 封装了所有子类共同遵循的算法框架
     */
    public final void prepareBeverageTemplate() {
        // 步骤 1 将水煮沸
        boilWater();
        // 步骤 2 泡制饮料
        brew();
        // 步骤 3 将饮料倒入杯中
        pourInCup();
        // isCustomerWantsCondiments() 是一个具体的方法
        if (isCustomerWantsCondiments()) {
            // 步骤 4 加入调味料
            addCondiments();
        }

    }

    /**
     * 基本方法,将水煮沸
     * 这是一个通用的,没有差别的方法,
     * 设置访问权限为私有,向下屏蔽细节
     */
    private void boilWater() {
        System.out.println("固定方法--将水煮沸");
    }

    /**
     * 基本方法,将饮料倒入杯中
     */
    private void pourInCup() {
        System.out.println("固定方法--将饮料倒入杯中");
    }

    /**
     * 须要在子类中可见,延迟到子类实现具体细节
     * 抽象的基本方法,泡制饮料
     */
    protected abstract void brew();

    /**
     * 加入调味料
     */
    protected abstract void addCondiments();

    /**
     * Hook,钩子函数,提供一个默认的或者空的实现
     * 具体子类可以自行决定是否挂钩,以及如何挂钩
     * 询问用户是否加入调料
     *
     * @return
     */
    protected boolean isCustomerWantsCondiments() {
        return true;
    }

}

编写两个实现类

1、茶类(加调料)

注意:须要重写加调料 isCustomerWantsCondiments() 方法。

public class Tea extends RefreshBeverage {
    @Override
    protected void brew() {
        System.out.println("特殊方法--用 80 度的水冲泡茶叶");
    }

    @Override
    protected void addCondiments() {
        System.out.println("特殊方法--加入柠檬");
    }

    /**
     * 子类通过覆盖的形式选择挂载钩子函数,
     * 钩子函数让子类更加灵活
     * @return
     */
    @Override
    protected boolean isCustomerWantsCondiments(){
        return false;
    }
}

2、咖啡类(不加调料)

不加调料是默认实现,所以无须覆盖 isCustomerWantsCondiments() 方法。

public class Coffee extends RefreshBeverage {
    @Override
    protected void brew() {
        System.out.println("特殊方法--用沸水冲泡咖啡");
    }

    @Override
    protected void addCondiments() {
        System.out.println("特殊方法--加入糖和牛奶");
    }
}

编写测试方法:

public class Main {
    
    public static void main(String[] args) {
        RefreshBeverage tea = new Tea();
        tea.prepareBeverageTemplate();

        System.out.println("------ 愉快的分割线 ------");

        RefreshBeverage coffee = new Coffee();
        coffee.prepareBeverageTemplate();
    }

}

运行结果:

推荐阅读