设计模式(九)—— 模版方法模式

一、含义

在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。通俗讲,就是当你的程序中,有多个类,方法是相同的,只是具体实现有差异时,就可以把这多个类使用模板方法模式进行封装。

二、要点

1.模板方法只定义了算法的步骤,把这些步骤的具体实现延迟到子类中进行。

2.模板方法的抽象类可以定义具体方法、抽象方法和钩子;抽象方法教给子类去实现。

3.钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要去覆盖它。

4.策略模式和模版模式有些相同,但策略是使用组合,模板是使用继承。

三、实战分析模板方法模式

老规矩,让我们先来看看这个设计模式的类图:

作为一个程序猿,晚上加班是不可避免的,有的时候加班熬不住,就需要借助外物来提提神,有的人喜欢喝茶提神,有的人喜欢喝咖啡来提神,这个时候就需要设计两个类,一个是茶,一个是咖啡,代码如下:

//咖啡类
public class Coffee {

    void prepareRecipe() {
        boilWater();
        brewCoffeeGrinds();
        pourInCup();
        addSugarAndMilk();
    }

    //把水煮沸
    public void boilWater() {
        System.out.println("Boiling water");
    }

    //用沸水冲泡咖啡
    public void brewCoffeeGrinds() {
        System.out.println("Dripping Coffee through filter");
    }

    //把咖啡倒进杯子
    public void pourInCup() {
        System.out.println("Pouring into cup");
    }

    //加糖和牛奶
    public void addSugarAndMilk() {
        System.out.println("Adding Sugar and Milk");
    }
}
//茶类
public class Tea {

    void prepareRecipe() {
        boilWater();
        steepTeaBag();
        pourInCup();
        addLemon();
    }
    
    //把水煮沸
    public void boilWater() {
        System.out.println("Boiling water");
    }

    //泡茶叶
    public void steepTeaBag() {
        System.out.println("Steeping the tea");
    }

    //把茶倒进杯子
    public void pourInCup() {
        System.out.println("Pouring into cup");
    }

    //加柠檬
    public void addLemon() {
        System.out.println("Adding Lemon");
    }
}

看代码可以知道,其实泡茶和泡咖啡都需要把水煮沸,倒进杯子,只是一个是加咖啡,加牛奶,一个是加茶叶,加柠檬,根据模版模式,可以将这两个对象进行处理,抽离出一个超类作为模板

//这是一个模板
public abstract class drinks {

    //将这个方法声明为final,不希望子类覆盖这个方法
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    //通用的方法直接在父类中实现,在这个例子中,就是烧水和倒进杯子
    public void boilWater() {
        System.out.println("Boiling water");
    }

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

    //需要由子类的去实现的方法,定义为抽象方法
    abstract void brew();

    abstract void addCondiments();

}

在重新定义茶和咖啡的类:

//新的茶类继承饮料
public class Tea extends Drinks {

    //将父类中的抽象方法具体化,来满足自己的实现
    @Override
    void brew() {
        System.out.println("Steeping the tea");
    }

    @Override
    void addCondiments() {
        System.out.println("Adding Lemon");
    }
}
//新的咖啡类继承自模板
public class Coffee extends Drinks {

    //将父类中的抽象方法具体化,来满足自己的实现
    @Override
    void brew() {
        System.out.println("Dripping Coffee through filter");
    }

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

但是在喝茶的时候,本人是不喜欢放柠檬片,只想保留茶的原汁原味,但有人的并不会放,这个时候就需要根据个人喜好去决定到底需不需要加柠檬片,这个时候我们就可以使用,模板模式中的钩子,那什么是钩子呢?

钩子是一种被声明在抽象类中的方法,但只有空和默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。

重新定义一下模板类,代码如下:

//这是一个模板
public abstract class Drinks {

    //将这个方法声明为final,不希望子类覆盖这个方法
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerDecision()) {
            addCondiments();
        }

    }

    //通用的方法直接在父类中实现,在这个例子中,就是烧水和倒进杯子
    public void boilWater() {
        System.out.println("Boiling water");
    }

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

    //需要由子类的去实现的方法,定义为抽象方法
    abstract void brew();

    abstract void addCondiments();

    //这是一个钩子,子类可以决定是否覆盖这个方法
    boolean customerDecision() {
        return true;
    }

}

在修改一下茶的类,加入一个钩子的实现

//新的茶类继承饮料
public class Tea extends Drinks {

    //将父类中的抽象方法具体化,来满足自己的实现
    @Override
    void brew() {
        System.out.println("Steeping the tea");
    }

    @Override
    void addCondiments() {
        System.out.println("Adding Lemon");
    }

    //覆盖钩子实现自己的定义
    @Override
    boolean customerDecision() {
        String answer = getUserImput();
        if (answer.toLowerCase().startsWith("y")) {
            return true;
        }

        return false;

    }

    private String getUserImput() {
        String answer = null;
        System.out.println("需要加柠檬片嘛?(y/n)");
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        try {
            answer = in.readLine();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        if (answer == null) {
            return "no";
        }
        return answer;
    }
}

编写一个测试来,测试一下

public class DrinksTest {

    public static void main(String[] args) {
        //创建茶和咖啡
        Tea tea = new Tea();
        Coffee coffee = new Coffee();
        System.out.println("prepare Tea:");
        tea.prepareRecipe();
        System.out.println("prepare Coffee:");
        coffee.prepareRecipe();
    }
}

测试结果如下:

prepare Tea:
Boiling water
Steeping the tea
Pouring into cup
需要加柠檬片嘛?(y/n)
y
Adding Lemon
prepare Coffee:
Boiling water
Dripping Coffee through filter
Pouring into cup
Adding Sugar and Milk

文章内容参考《Head First 设计模式》

猜你喜欢

转载自blog.csdn.net/huxiaodong1994/article/details/84574896