• 设计模式---模板设计模式(java)


    模板设计模式

    1)基本定义

    定义:在一个抽象类中公开定义执行它的方法的方式/模板,子类可以重写方法的实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型设计模式。
    意图:定义一个操作种的算法骨架,将一些具体步骤的实现延迟到子类中
    解决问题:一些方法通用,却在每一个子类重写这些方法
    关键代码:算法骨架在抽象类实现(有时使用final修饰以禁止子类重写以防止恶意修改),一些具体步骤在子类类实现
    优点:封装不变部分、扩展可变部分;提取公共代码、便于维护;行为由父类统一控制,子类实现
    缺点:每一个不同的实现都需要一个子类实现,导致类数量增加,系统变得庞大
    场景:有多个子类共有的方法,逻辑相同;重要的、复杂的方法,可以考虑作为模板方法

    设计模式类图

     2)例子

    以悍马车模型为例,类图如下:

    实现代码如下:

    /** 悍马车模型 **/
    public abstract class HummerModel {
        /** 启动 **/
        public abstract void start();
        /** 停止 **/
        public abstract void stop();
        /** 喇叭鸣叫 **/
        public abstract void alarm();
        /** 引擎轰鸣 **/
        public abstract void engineBoom();
        /** 汽车跑起来 **/
        public abstract void run();
    }
    
    public class HM1 extends HummerModel{
        @Override
        public void start() {
            System.out.println("HM1 start");
        }
        @Override
        public void stop() {
            System.out.println("HM1 stop");
        }
        @Override
        public void alarm() {
            System.out.println("HM1 alarm");
        }
        @Override
        public void engineBoom() {
            System.out.println("HM1 engineBoom");
        }
        @Override
        public void run() {
            System.out.println("HM1 run");
            alarm();
            start();
            engineBoom();
            stop();
        }
    }
    
    public class HM2 extends HummerModel{
        @Override
        public void start() {
            System.out.println("HM2 start");
        }
        @Override
        public void stop() {
            System.out.println("HM2 stop");
        }
        @Override
        public void alarm() {
            System.out.println("HM2 alarm");
        }
        @Override
        public void engineBoom() {
            System.out.println("HM2 engineBoom");
        }
        @Override
        public void run() {
            System.out.println("HM2 run");
            alarm();
            start();
            engineBoom();
            stop();
        }
    }

    观察上面代码,发现明显的问题,让汽车跑起来的方法run()所有的车型应该是一样的,应该在抽象类中,修改类图如下:

    新的实现代码如下:

    /** 悍马车模型 **/
    public abstract class HummerModel {
        /** 启动 **/
        public abstract void start();
        /** 停止 **/
        public abstract void stop();
        /** 喇叭鸣叫 **/
        public abstract void alarm();
        /** 引擎轰鸣 **/
        public abstract void engineBoom();
        /** 汽车跑起来 **/
        public final void run() {
            System.out.println("HM2 run");
            alarm();
            start();
            engineBoom();
            stop();
        }
    }
    
    public class HM1 extends HummerModel{
        @Override
        public void start() {
            System.out.println("HM1 start");
        }
        @Override
        public void stop() {
            System.out.println("HM1 stop");
        }
        @Override
        public void alarm() {
            System.out.println("HM1 alarm");
        }
        @Override
        public void engineBoom() {
            System.out.println("HM1 engineBoom");
        }
    }
    
    public class HM2 extends HummerModel{
        @Override
        public void start() {
            System.out.println("HM2 start");
        }
        @Override
        public void stop() {
            System.out.println("HM2 stop");
        }
        @Override
        public void alarm() {
            System.out.println("HM2 alarm");
        }
        @Override
        public void engineBoom() {
            System.out.println("HM2 engineBoom");
        }
    }

    调用如下:

    public class HMTest {
        public static void main(String[] args) {
            HummerModel h1 = new HM1();
            h1.run();
            HummerModel h2 = new HM2();
            h2.run();
        }
    }

    仔细思考上面的实现,会发现以下问题:
    1)客户要关心模型的启动,停止,鸣笛,引擎声音吗?他只要在run的过程中,听到或看到就成了,暴露那么多方法干嘛呢?把抽象类上的四个方法设置为protected访问权限
    2)run方法设置成final,子类不可修改,但不是所有的车型都是想鸣喇叭,且客户更想在想要鸣时就鸣,由自己决定,这就可以使用钩子方法。
    修改类图如下:

    实现代码如下:

    /** 悍马车模型 **/
    public abstract class HummerModel {
        /** 启动 **/
        protected abstract void start();
        /** 停止 **/
        protected abstract void stop();
        /** 喇叭鸣叫 **/
        protected abstract void alarm();
        /** 引擎轰鸣 **/
        protected abstract void engineBoom();
        /** 汽车跑起来 **/
        public final void run() {
            System.out.println("HM2 run");
            if(isAlarm())
                alarm();
            start();
            engineBoom();
            stop();
        }
        /** 钩子方法,是否鸣笛 ,默认实现**/
        protected boolean isAlarm() {
            return true;
        }
    }
    
    public class HM1 extends HummerModel{
        @Override
        protected void start() {
            System.out.println("HM1 start");
        }
        @Override
        protected void stop() {
            System.out.println("HM1 stop");
        }
        @Override
        protected void alarm() {
            System.out.println("HM1 alarm");
        }
        @Override
        protected void engineBoom() {
            System.out.println("HM1 engineBoom");
        }
        
        private boolean isAlarm = false;
        /** 子类自己提供设置是否鸣笛的接口 **/
        public void setIsAlarm(boolean isAlarm) {
            this.isAlarm = isAlarm;
        }
        /** 重写钩子方法,是否鸣笛 **/
        protected boolean isAlarm() {
            return isAlarm;
        }
    }
    
    public class HM2 extends HummerModel{
        @Override
        protected void start() {
            System.out.println("HM2 start");
        }
        @Override
        protected void stop() {
            System.out.println("HM2 stop");
        }
        @Override
        protected void alarm() {
            System.out.println("HM2 alarm");
        }
        @Override
        protected void engineBoom() {
            System.out.println("HM2 engineBoom");
        }
        /** 不提供钩子方法重写,不提供设置是否鸣笛的接口 **/
    }

    调用如下:

    public class HMTest {
        public static void main(String[] args) {
            HM1 h1 = new HM1();
            h1.setIsAlarm(true);//这里setIsAlarm方法不在父类中,只可以使用子类对象调用
            h1.run();
            HummerModel h2 = new HM2();
            h2.run();
        }
    }

    注意:callback和“钩子”是两个完全不同的概念,callback是指:由我们自己实现的,但是预留给系统调用的函数,我们自己是没有机会调用的,但是我们知道系统在什么情况下会调用该方法。而“钩子”是指:声明在抽象类中的方法,只有空的或默认的实现,通常应用在模板设计模式中,让子类可以对算法的不同点进行选择或挂钩,要不要挂钩由子类决定。

    由例子可以看出,模板设计核心原理是通过继承、重写、实现来实现父类调用子类方法。

  • 相关阅读:
    Oracle根据【日期】组,其他条件根据PIVOT行转列。使每个日期条件关联的其他数据只有一行。
    ORACLE数据库,数据量大,转移数据到备份表语句
    C#解析"a=1&b=2&c=3"字符串,微信支付返回字符串,替换<br>为&
    dataTable的数据,调试的时候点放大镜就看到了啊啊啊!
    Debug和Release 老程序啊 调试之前 区分一下啊
    FastReport.NET
    grpc 实现微服务生态笔记
    金木水火土
    shell 指令分析nginx 日志qps
    idea中使用tomcat 方式启动spring boot项目
  • 原文地址:https://www.cnblogs.com/ShouWangYiXin/p/10862408.html
Copyright © 2020-2023  润新知