模板方法模式.

一、概念

  • 模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
  • 解析:模板方法模式用来创建一个算法的模板。什么是模板?模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这可以确保算法的结构保持不变,同时由子类提供部分实现。
  • 角色:
     1、抽象模板(Abstract Template):定义模板方法,规范了一组步骤,其中父类可以实现其中的某些步骤,需要由子类实现的步骤声明为 abstract。
     2、具体模板(Concrete Template):继承抽象父类,实现抽象父类中声明为 abstract 的方法。

avatar

二、Demo 实现

我们打算定义一个咖啡因饮料冲泡流程,把流程中相同的步骤放在一起,同时,不同的饮料还能有自己的具体实现。

1、抽象模板

public abstract class CaffeineBeverage {

    /**
     * @Description 
     * 1、模板方法,定义算法的步骤。
     * 2、我们一般把模板方法定义成 final,不希望子类覆盖。
     */
    public final void prepareRecipe() {
        //1、把水煮沸
        boilWater();
        //2、用热水泡饮料(具体什么饮料未知)
        brew();
        //3、把饮料倒进杯子
        pourIncup();
        //4、往饮料中添加调料(具体什么调料未知)
        if (wantAddCondiments()) {
            addCondiments();
        }

    }

    /**
     * @Description 需要子类提供实现的步骤定义为抽象
     */
    protected abstract void brew();

    protected abstract void addCondiments();

    /**
     * @Description 由父类提供实现,又不希望被子类重写的,封装成 private。
     */
    private void boilWater() {
        System.out.println("Boiling water");
    }

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


    /**
     * @Description 钩子方法
     * 1、钩子是一种被声明在抽象类中的方法,当只有空的或者默认的实现。
     * 2、钩子的存在,可以让子类有能力对算法的不同点进行挂钩(重写)。
     * 3、要不要挂钩(重写),由子类自己决定。
     */
    protected boolean wantAddCondiments() {
        return true;
    }
}

这里,我们定义了一个抽象模板,规范了具体的冲泡流程 —— 把水煮沸->泡饮料->倒杯子->添加调料。在抽象模板中,我们有三种类型的方法,一种需要子类提供实现的,我们定义为 abstract ;一种父类提供实现,又不希望被子类覆盖的,我们定义为 private;还有一种称为钩子方法,父类提供默认实现,子类可自由选择是否重写父类的方法。

2、具体模板

public class Coffee extends CaffeineBeverage {
    @Override
    protected void brew() {
        System.out.println("Dripping Coffee through filter");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding Suger and Milk");
    }
}
public class Tea extends CaffeineBeverage {

    @Override
    protected void brew() {
        System.out.println("Steeping the tea");
    }

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

    /**
    * @Description 子类重写钩子方法
    */
    @Override
    protected boolean wantAddCondiments() {
        return false;
    }
}

具体模板继承了抽象模板,实现了抽象模板中定义为 abstract 的步骤方法,并可以自己选择是否重写钩子方法。

3、测试

public class Test {

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

avatar

三、总结

  • 模板方法模式的通用类图非常简单,仅仅使用了Java的继承机制,但它是一个非常广泛的模式。
  • 优点:
     1、封装不变部分,扩展可变部分。把认为不变部分的算法封装到父类中实现,而可变部分的则可以通过继承来继续扩展。可以将代码最大复用化。
     2、父类规范算法行为,子类提供完整实现。
  • 使用场景:
     1、多个子类有共有的方法,并且逻辑基本相同。
     2、一次性实现一个算法的不变部分,并且将可变的行为留给子类来完成。
  • 为防止子类改变模板方法中的算法,可以将模板方法声明为 final。
  • 策略模式和模板方法模式都封装算法,但是策略模式使用的是组合,模板方法模式使用的是继承。
  • 工厂方法是模板方法的一种特殊版本。
  • 模板方法导致一种反向的控制结构,这种结构有时被称为“好莱坞原则“。
  • 好莱坞原则:别调用我们,我们会调用你。在好莱坞原则之下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式是“别调用我们,我们会调用你”。
  • 低层组件可以调用高层组件中的方法(实际上子类会常常调用其从父类中继承所来的方法),但是我们要做的是要避免让高层和底层组件之间有明显的环状依赖
posted @ 2018-12-21 09:35  JMCui  阅读(356)  评论(0编辑  收藏  举报