Head First设计模式读书笔记七 第八章 模板方法模式

版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/u011109881/article/details/82453771

本文示例代码材料源自Head First设计模式
以前整理自己整理的链接:
https://blog.csdn.net/u011109881/article/details/60594985

简介

模板方法模式很容易理解。思想基本如下:先在父类规定了具体的算法步骤以及算法顺序。父类可以给出部分步骤的具体实现,也可以都只给出方法框架,没有具体实现。在子类具体实现各个步骤的方法,但是各个步骤间的顺序在父类已经确定,子类无法通常不应该更改。如果规定算法顺序的方法在父类被定义成final,则子类就无法更改了。具体实现,根据实际需求确定。

  • 其目的一方面是减少代码重复,达到代码复用的目的
  • 另一方面也可以在父类控制和限制子类的动作。
  • 父类(泛化类)规定了一个算法框架,大多数时候,只要修改子类即可。
  • 将具体算法和实现相分离,各司其职(单一职责),基类负责算法设计,(某些情况也会涉及一点实际实现),子类专司具体算法实现。

模板方法模式实例1

以泡茶和泡咖啡为例。他们是一个相近的例子。
泡茶的步骤如下:
1. 把水煮沸
2. 用沸水泡茶叶
3. 把茶倒进杯子
4. 加柠檬
而泡咖啡的步骤:
1. 把水煮沸
2. 用沸水泡咖啡
3. 把咖啡倒进杯子
4. 加糖和牛奶
用代码很容易表示这段描述

public class Coffee {
    public void prepareRecipe() {
        boilWater();
        brewCoffeeGrinds();
        pourInCup();
        addSugarAndMilk();
    }

    private void addSugarAndMilk() {
        System.out.println("addSugarAndMilk");
    }

    private void pourInCup() {
        System.out.println("pourInCup");
    }

    private void brewCoffeeGrinds() {
        System.out.println("brewCoffeeGrinds");
    }

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

}
public class Tea {
    public void prepareRecipe() {
        boilWater();
        steepTeaBag();
        pourInCup();
        addLemon();
    }


    private void addLemon() {
        System.out.println("addLemon");
    }


    private void steepTeaBag() {
        System.out.println("steepTeaBag");
    }


    private void pourInCup() {
        System.out.println("pourInCup");
    }

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

}

代码很简单,但是看上去似乎存在一些冗余代码,让我们做一下结构优化。
增加一个抽象父类,然后对coffee和tea进行一些改进。

public abstract class CaffeineBeverage {
    public final void prepareRecipe() {
        //final修饰,子类无法改写
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    public abstract void brew() ;

    public abstract void addCondiments() ;

    private void pourInCup() {
        System.out.println("pourInCup");
    }

    private void boilWater() {
        System.out.println("boilWater");
    }
}
public class Coffee extends CaffeineBeverage{

    public void brew() {
        System.out.println("brewCoffeeGrinds");
    }

    public void addCondiments() {
        System.out.println("addSugarAndMilk ");
    }
}
public class Tea extends CaffeineBeverage {

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

    public void addCondiments() {
        System.out.println("addLemon");
    }

}
public class Test {

    public static void main(String[] args) {
        CaffeineBeverage tea = new Tea();
        tea.prepareRecipe();
        CaffeineBeverage coffee = new Coffee();
        coffee.prepareRecipe();
    }

}

测试结果:

boilWater
steepTeaBag
pourInCup
addLemon
boilWater
brewCoffeeGrinds
pourInCup
addSugarAndMilk 

可以看到,CaffeineBeverage是coffee和tea的一个泛化。

关于模板方法的hook,实例1的修改版

模板方法中基类可以存在一些称之为hook的方法,这类方法在算法步骤中属于可选部分。他们的调用与否通常由子类决定。举个例子:
对基类稍作修改

public abstract class CaffeineBeverage {
    public final void prepareRecipe() {
        //final修饰,子类无法改写
        boilWater();
        brew();
        pourInCup();
        if(customerWantsCondiments()){
            addCondiments();
        }else{
            System.out.println("customer don't need condiments");
        }
    }

    public abstract void brew() ;

    public abstract void addCondiments() ;

    private void pourInCup() {
        System.out.println("pourInCup");
    }

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

    boolean customerWantsCondiments(){
        //该方法为hook方法,子类可以改写它,也可以不改。
        return true;
    }
}

对于实现类,可根据需要决定是否覆盖hook接口
比如Coffee类就修改了

public class Coffee extends CaffeineBeverage{

    public void brew() {
        System.out.println("brewCoffeeGrinds");
    }

    public void addCondiments() {
        System.out.println("addSugarAndMilk ");
    }

    boolean customerWantsCondiments() {
        String answer = getUserInput();
        if(answer.startsWith("y")||answer.startsWith("Y")){
            return true;
        }else{
            return false;
        }
    }

    String getUserInput(){
        System.out.println("do you want condiments?(y/n)");
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String answer = null;
        try {
            answer = in.readLine();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return answer;
    }
}

而Tea类则没有

public class Tea extends CaffeineBeverage {

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

    public void addCondiments() {
        System.out.println("add condiment directly");
        System.out.println("addLemon");
    }

}

测试类则无需变动

public class Test {

    public static void main(String[] args) {
        CaffeineBeverage tea = new Tea();
        tea.prepareRecipe();
        System.out.println();
        CaffeineBeverage coffee = new Coffee();
        coffee.prepareRecipe();
    }

}

从测试结果可以看到,如果是tea则无需问是否添加调料,而在coffee时,却需要用户确认。
这里写图片描述
这里写图片描述

实际使用的模板方法很可能并不像上面的例子这样容易辨识。比如,如果我们有一个User的ArrayList,需要对User的age进行排序,那么可以用如下方式实现
大概框架:

        ArrayList<User> list = new ArrayList();
        list.sort(new Comparator<User>() {

            public int compare(User o1, User o2) {
                return 0;
            }
        });

这里其实用到的就是模板方法,很费解吧。
以下纯属个人理解,如有错误请指出:
ArrayList的排序是用Arrays的静态sort方法来实现的
这里的写法其实是实现了在Comparator中的hook接口:compare方法
sort中会使用两个comparable的对象进行compare,而实际compare时则调用的是我们上面实现的compare方法。
模板方法用的高深起来,也很难懂呢,所以其他有很多我们没有注意到的地方,都隐藏着设计模式的影子,等着我们发现呀。

猜你喜欢

转载自blog.csdn.net/u011109881/article/details/82453771
今日推荐