Java学习——模板设计模式——抽象类的实际应用

设计模式的精髓:解耦。而模板设计模式是通过第三方进行解耦

什么是内聚、解耦大家可以看一下博主 小异常 的博文:https://blog.csdn.net/sun8112133/article/details/81040275

模板设计模式:(基于抽象类)在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类再不改变算法的前提下,重新定义算法中的某些步骤。

通俗点的理解就是 :完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。

一个经典的模板设计模式的例子

星巴克咖啡冲泡法

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

星巴克茶冲泡法

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

用代码简单实现就是

class Coffee{
    void prepareRecipe(){
        boilWater();
        brewCoffeeGrings();
        pourInCap();
        addSugarAndMilk();
    }
    public void boilWater(){
        System.out.println("将水煮沸");
    }
    public void brewCoffeeGrings(){
        System.out.println("冲泡咖啡");
    }
    public void pourInCap(){
        System.out.println("将咖啡倒进杯子");
    }
    public void addSugarAndMilk(){
        System.out.println("加入糖和牛奶");
    }
}
class Tea {
    void prepareRecipe() {
    boilWater();
    steepTeaBag();
    pourInCup();
    addLemon();
    }
    public void boilWater() {
        System.out.println("将水煮沸");
    }
    public void steepTeaBag() {
        System.out.println("冲泡茶");
    }
    public void pourInCup() {
        System.out.println("将茶倒进杯子");
    }
    public void addLemon() {
        System.out.println("加柠檬");
    }
}
class Test{
    public static void main(String[] args) {
        Coffee coffee = new Coffee();
        coffee.prepareRecipe();
        Tea tea = new Tea();
        tea.prepareRecipe();
    }
}

让我们先从冲泡法入手。观察咖啡和茶的冲泡法我们会发现,两种冲泡法都采用了相同的算法:
1. 将水煮沸
2. 用热水泡饮料
3. 把饮料倒进杯子
4. 在饮料内加入适当的调料

实际上,浸泡(steep)和冲泡(brew)差异并不大。因此我们给它一个新的方法名称brew(),这样我们无论冲泡的是何种饮
料都可以使用这个方法。同样的,加糖、牛奶还是柠檬也很相似,都是在饮料中加入其它调料,因此我们也给它一
个通用名称addCondiments()。我们可以将这些重复的操作写在一个抽象类中,不同的地方写抽象方法,让实现的操作延迟到子类去具体完成。

/**
* 咖啡因饮料是一个抽象类
*/
abstract class  CaffeineBeverage{
    /**
    * 声明为final的原因是我们不希望子类覆盖这个方法!
    */
    final void prepareRecipe(){
        boilWater();
        brew();
        pourInCap();
        addCondiments();
    }
    /**
    * 咖啡和茶处理这些方法不同,因此这两个方法必须被声明为抽象,留给子类实现
    */
    abstract void brew();
    abstract void addCondiments();
    public void boilWater(){
        System.out.println("将水煮沸");
    }
    public void pourInCap(){
        System.out.println("将饮料倒进杯子");
    }
}
class Coffee extends CaffeineBeverage{
    public void brew(){
        System.out.println("冲泡咖啡");
    }
    public void addCondiments(){
        System.out.println("加入糖和牛奶");
    }
}
class Tea extends CaffeineBeverage{
    public void brew() {
        System.out.println("冲泡茶");
    }
    public void addCondiments() {
        System.out.println("加柠檬");
    }
}
class Test{
    public static void main(String[] args) {
        CaffeineBeverage coffee = new Coffee();
        coffee.prepareRecipe();
        CaffeineBeverage tea = new Tea();
        tea.prepareRecipe();
    }
}

此时的类图:

我们刚刚实现的就是模板设计模式,它包含了实际的"模板方法"

模板方法定义了一个算法的步骤,并允许子类为一个或者多个步骤提供具体实现。

不好的茶或咖啡实现                                                                      模板方法提供的咖啡因饮料
Coffee或Tea主导一切,控制算法                                                  由超类主导一切,它拥有算法,并且保护这个算法
Coffee与Tea之间存在重复代码                                                      有超类的存在,因此可以将代码复用最大化
对于算法所做的代码改变,需要打开各个子类修改很多地方         算法只存在一个地方,容易修改
弹性差,新种类的饮料加入需要做很多工作                                   弹性高,新饮料的加入只需要实现自己的冲泡和加料方法即可
算法的知识和它的实现分散在许多类中                                          超类专注于算法本身,而由子类提供完整的实现。

一个完整的模板模式超类的定义:

/**
* 基类声明为抽象类的原因是
* 其子类必须实现其操作
*/
abstract class AbstractClass {
    /**
    * 模板方法,被声明为final以免子类改变这个算法的顺序
    */
    final void templateMethod() {
    }
    /**
    * 具体操作延迟到子类中实现
    */
    abstract void primitiveOperation1();
    abstract void primitiveOperation2();
    /**
    * 具体操作且共用的方法定义在超类中,可以被模板方法或子类直接使用
    */
    final void concreteOperation() {
    // 实现
    }
    /**
    * 钩子方法是一类"默认不做事的方法"
    * 子类可以视情况决定要不要覆盖它们。
    */
    void hook() {
    // 钩子方法
    }
}

什么是钩子方法:钩子方法在我理解就是当我们需要用法从其他地方才能获取的值时,我们需要一个钩子把它钩过来,在下面的代码里就是我们从主类钩过来了用户的输入,再在其他类中根据这个输入做具体的操作,这样使我们的程序更加灵活。

import java.util.Scanner;
abstract class  CaffeineBeverage{

    final void prepareRecipe(){
        boilWater();
        brew();
        pourInCap();
        if(customerWantsCondiments()){
            addCondiments();
        }
    }
    abstract void brew();
    abstract void addCondiments();
    public void boilWater(){
        System.out.println("将水煮沸");
    }
    public void pourInCap(){
        System.out.println("将饮料倒进杯子");
    }
    /**
    * 钩子方法
    * 超类中通常是默认实现
    * 子类可以选择性的覆写此方法
    * @return
    */
    public boolean customerWantsCondiments(){
        return true;
    }
}
class Coffee extends CaffeineBeverage{
    public void brew(){
        System.out.println("冲泡咖啡");
    }
    public void addCondiments(){
        System.out.println("加入糖和牛奶");
    }
    /**
    * 子类覆写了钩子函数,实现自定义功能
    * @return
    */
    public boolean customerWantsCondiments(){
        String str = getUserInput();
        if(str.equals("y"))
            return true;
        else
            return false;
    }
    private String getUserInput() {
        String answer = null;
        System.out.println("您想要在咖啡中加入牛奶或糖吗 (y/n)?");
        Scanner scanner = new Scanner(System.in);
        answer = scanner.nextLine();
        return answer;
    }
}
class Test{
    public static void main(String[] args) {
        CaffeineBeverage coffee = new Coffee();
        coffee.prepareRecipe();
    }
}

猜你喜欢

转载自blog.csdn.net/eve8888888/article/details/83240540