模板方法模式

概念:实现一些操作时,整体步骤很固定,但是其中一小部分是变化的,这时候就可以使用模板方法模式,将容易变化的部分抽离出来,供子类实现。

特点:
    1.封装不变部分,扩展可变部分
    2.提取公共部分代码,便于维护
    3.行为由父类控制,子类实现

缺点:
        我们一般的设计习惯是抽象类负责声明最抽象、最一般的事物属性和方法,实现类来完成具体事物属性和方法,但是模板方法模式却反过来了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,在复杂的项目会带来代码阅读的难度,会让新手产生不适感。

 

应用场景:servlet

1.多个子类有公有的方法,并且逻辑基本相同时。重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。

2.重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。

注意:抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则,不需要暴露的属性或方法尽量不要设置为protected类型。实现类若非必要,尽量不要扩大父类中的访问权限。为了防止恶意操作,模板方法一般设置为final。

 

代码:

public abstract class HummerModel {
    /*
     * 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正
     * 是要能够发动起来,那这个实现要在实现类里了
     */
    public abstract void start();
    //能发动,还要能停下来,那才是真本事
    public abstract void stop();
    //喇叭会出声音,是滴滴叫,还是哔哔叫
    public abstract void alarm();
    //引擎会轰隆隆地响,不响那是假的
    public abstract void engineBoom();
    //那模型应该会跑吧,别管是人推的,还是电力驱动的,总之要会跑
    
    //开动起来
    final public void run(){
        //先发动汽车
        this.start();
        //引擎开始轰鸣
        this.engineBoom();
        //然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
        this.alarm();
        //到达目的地就停车
        this.stop();
    }
}
public class HummerH1Model extends HummerModel {
    //H2型号的悍马车鸣笛
    public void alarm() {
        System.out.println("悍马H1鸣笛...");
    }
    //引擎轰鸣声
    public void engineBoom() {
        System.out.println("悍马H1引擎声音是这样在...");
    }
    //汽车发动
    public void start() {
        System.out.println("悍马H1发动...");
    }
    //停车
    public void stop() {
        System.out.println("悍马H1停车...");
    }

}
public class HummerH2Model extends HummerModel {
    //H2型号的悍马车鸣笛
    public void alarm() {
        System.out.println("悍马H2鸣笛...");
    }
    //引擎轰鸣声
    public void engineBoom() {
        System.out.println("悍马H2引擎声音是这样在...");
    }
    //汽车发动
    public void start() {
        System.out.println("悍马H2发动...");
    }
    //停车
    public void stop() {
        System.out.println("悍马H2停车...");
    }

}
public class Client {
    public static void main(String[] args) {
        HummerH1Model h1Model = new HummerH1Model();
        HummerH2Model h2Model = new HummerH2Model();
        h1Model.run();
        h2Model.run();
    }
}

结果:

悍马H1发动...
悍马H1引擎声音是这样在...
悍马H1鸣笛...
悍马H1停车...
悍马H2发动...
悍马H2引擎声音是这样在...
悍马H2鸣笛...
悍马H2停车...

通过上述代码,可以看出,我们首先定义一个抽象的模板方法类,然后将不变的方法实现了也就是run()方法,将容易变化的方法定义成抽象方法,然后由子类去实现这些易变的操作,就是其他常规的抽象方法如start()方法等都在具体的子类实现,子类的实现不同,会导致父类的执行结果不同。

扩展:

如果此时来了一个新需求,模型1的喇叭我要手动控制,模型2的喇叭我不要了,那么我们可以修改代码:

public abstract class HummerModel {
     /*
      * 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正
      * 是要能够发动起来,那这个实现要在实现类里了
      */
     protected abstract void start();
     //能发动,那还要能停下来,那才是真本事
     protected abstract void stop();
     //喇叭会出声音,是滴滴叫,还是哔哔叫
     protected abstract void alarm();
     //引擎会轰隆隆的响,不响那是假的
     protected abstract void engineBoom();
     //那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
     final public void run() {
             //先发动汽车
             this.start();
             //引擎开始轰鸣
             this.engineBoom();
             //要让它叫的就是就叫,喇嘛不想让它响就不响
             if(this.isAlarm()){
                      this.alarm();
     }
             //到达目的地就停车
             this.stop();
     }
     //钩子方法,默认喇叭是会响的
     protected boolean isAlarm(){
             return true;
     }
}
public class HummerH1Model extends HummerModel {
    private boolean alarmFlag = true; //要相响喇叭
    //H2型号的悍马车鸣笛
    public void alarm() {
        System.out.println("悍马H1鸣笛...");
    }
    //引擎轰鸣声
    public void engineBoom() {
        System.out.println("悍马H1引擎声音是这样在...");
    }
    //汽车发动
    public void start() {
        System.out.println("悍马H1发动...");
    }
    //停车
    public void stop() {
        System.out.println("悍马H1停车...");
    }

    protected boolean isAlarm(){
        return this.alarmFlag;
    }
    //要不要响喇叭,由客户决定
    public void setAlarm(boolean isAlarm){
        this.alarmFlag = isAlarm;
    }
}
public class HummerH2Model extends HummerModel {
    //H2型号的悍马车鸣笛
    public void alarm() {
        System.out.println("悍马H2鸣笛...");
    }
    //引擎轰鸣声
    public void engineBoom() {
        System.out.println("悍马H2引擎声音是这样在...");
    }
    //汽车发动
    public void start() {
        System.out.println("悍马H2发动...");
    }
    //停车
    public void stop() {
        System.out.println("悍马H2停车...");
    }
    //模型2关闭喇叭功能
    protected boolean isAlarm(){
        return false;
    }
}
public class Client {
    public static void main(String[] args) throws IOException {
        System.out.println("-------H1型号悍马--------");
        System.out.println("H1型号的悍马是否需要喇叭声响?0-不需要1-需要");
        String type=(new BufferedReader(new InputStreamReader(System.in))).readLine();
        HummerH1Model h1=new HummerH1Model();
        if(type.equals("0")){
            h1.setAlarm(false);
        }
        h1.run();
        System.out.println("-------H2型号悍马--------");
                HummerH2Model h2=new HummerH2Model();
        h2.run();
    }
}

结果1:

-------H1型号悍马--------
H1型号的悍马是否需要喇叭声响?0-不需要1-需要
0
悍马H1发动...
悍马H1引擎声音是这样在...
悍马H1停车...
-------H2型号悍马--------
悍马H2发动...
悍马H2引擎声音是这样在...
悍马H2停车...

--------------------------------------------------------------------------------------------------------------------------------

结果2:

H1型号的悍马是否需要喇叭声响?0-不需要1-需要
1
悍马H1发动...
悍马H1引擎声音是这样在...
悍马H1鸣笛...
悍马H1停车...
-------H2型号悍马--------
悍马H2发动...
悍马H2引擎声音是这样在...
悍马H2停车...

这样我们就解决了这个新需求,在抽象类中isAlarm的返回值就影响了模板方法的执行结果,该方法就叫做钩子方法。由子类的一个返回值决定了公共部分的执行结果,我们可以按照自己的需要去控制功能的关闭,有了钩子函数的模板方法才算是完美的模板方法。


                                                                                                                         -----------------------例子来源于《设计模式之禅》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值