深入理解设计模式之模板模式

我们平时办理入职的流程是:填写入职登记表->打印简历->复印学历->复印身份证->签订劳动合同->建立花名册->办理工牌->安排工位。我平时在家里炒菜的流程是:洗锅->点火->热锅->上油->下原料->翻炒->放调料->出锅。以上这些都是模板模式的体现。

模板模式又叫模版方法模式(Template Method Pattern),是指定义一个算法的骨架,并允许子类为一个或多个步骤提供实现。模板模式使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤,属于行为型设计模式。模版模式适用于以下场景:

(1)一次性实现算法的不变部分,并将可变的行为留给子类来实现。

(2)各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。

下面我们写一个汽车类,来看代码实现:

 抽象模型:

public abstract class HummerModel {
    //能发动
    public abstract void start();

    //能停下来
    public abstract void stop();

    //喇叭会出声音
    public abstract void alarm();

    //引擎会轰隆隆地响
    public abstract void engineBoom();
    
    //会跑吧
    public final void run() {
        //先发动汽车
        this.start();
        //引擎开始轰鸣
        this.engineBoom();
        //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭 
        this.alarm();
        //到达目的地就停车 
        this.stop();
    }
}

在抽象类中,我们定义了悍马模型都必须具有的特质:能够发动、停止,喇叭会响,引擎可以轰鸣,而且还可以停止。每个型号的悍马实现是不同的,H1型号的悍马如代码:

public class HummerH1Model extends HummerModel{

    @Override
    public void start() {
        System.out.println("悍马H1启动.....");
    }

    @Override
    public void stop() {
        System.out.println("悍马H1停车.....");
    }

    @Override
    public void alarm() {
        System.out.println("悍马H1鸣笛.....");
    }

    @Override
    public void engineBoom() {
        System.out.println("悍马H1引擎Boom.....");
    }
    
}

H2型号实现: 

public class HummerH2Model extends HummerModel{

    @Override
    public void start() {
        System.out.println("悍马H2启动.....");
    }

    @Override
    public void stop() {
        System.out.println("悍马H2停车.....");
    }

    @Override
    public void alarm() {
        System.out.println("悍马H2鸣笛.....");
    }

    @Override
    public void engineBoom() {
        System.out.println("悍马H2引擎Boom.....");
    }

}

场景类:

class Client{
    public static void main(String[] args) {
        HummerModel hummerH2Model = new HummerH2Model();
        //H2模型演示
        hummerH2Model.run();
    }
}

结果如下:

 模版方法通用类图

 

模板方法模式非常简单,仅仅使用了Java的继承机制,但它是一个应用非常广泛的模式。其中,AbstractClass叫做抽象模板。

注意 :为了防止恶意的操作,一般模板方法都加上final关键字,不允许被覆写。

到目前为止,两个模型都运行的很好,但是突然有一天老大给我说车子不行了,一启动就滴滴滴响,客户说H1模型的汽车想让他响再响,那怎么解决呢?我们来看类图:

来看抽象类实现:

public abstract class HummerModel {

    //钩子方法,默认是回响的
    protected boolean isAlarm() {
        return true;
    }

    //能发动
    protected abstract void start();

    //能停下来
    protected abstract void stop();

    //喇叭会出声音
    protected abstract void alarm();

    //引擎会轰隆隆地响
    protected abstract void engineBoom();

    //会跑吧
    protected final void run() {
        //先发动汽车
        this.start();
        //引擎开始轰鸣
        this.engineBoom();
        //想叫再叫
        if (isAlarm()) {
            this.alarm();
        }
        //到达目的地就停车
        this.stop();
    }
}

在抽象类中,isAlarm()是一个实现方法。其作用是模板方法根据其返回值决定是否要响喇叭,子类可以覆写该返回值,由于H1型号的喇叭是想让它响就响,不想让它响就不响, 由人控制,来看代码:

public class HummerH1Model extends HummerModel{
    private boolean isAlarm = true;
    @Override
    public void start() {
        System.out.println("悍马H1启动.....");
    }

    @Override
    public void stop() {
        System.out.println("悍马H1停车.....");
    }

    @Override
    public void alarm() {
        System.out.println("悍马H1鸣笛.....");
    }

    @Override
    public void engineBoom() {
        System.out.println("悍马H1引擎Boom.....");
    }

    @Override
    public boolean isAlarm() {
        return this.isAlarm;
    }
    public void setAlarm(boolean isAlarm){
        this.isAlarm = isAlarm;
    }
}

场景类:

class Client{
    public static void main(String[] args) {
        System.out.println("-------H1型号悍马--------");
        System.out.println("H1型号的悍马是否需要喇叭声响?0-不需要 1-需要");
        String type = new Scanner(System.in).next();
        HummerH1Model hummerH1Model = new HummerH1Model();
        if (type.equals("0")){
            hummerH1Model.setAlarm(false);
        }
        hummerH1Model.run();
    }
}

结果如下:

 

 看到没,H1型号的悍马是由客户自己控制是否要响喇叭,也就是说外界条件改变,影响到模板方法的执行。在我们的抽象类中isAlarm()的返回值就是影响了模板方法的执行结果,该方法就叫做钩子方法(Hook Method)。有了钩子方法模板方法模式才算完美,大家可以想想,由子类的一个方法返回值决定公共部分的执行结果,是不是很有吸引力呀!

模板方法模式就是在模板方法中按照一定的规则和顺序调用基本方法,具体到前面那个例子,就是run()方法按照规定的顺序(先调用start(),然后再调用engineBoom(),再调用 alarm(),最后调用stop() )调用本类的其他方法,并且由isAlarm()方法的返回值确定run()中的执行顺序变更。

你可能感兴趣的:(深入理解设计模式,模板方法模式)