模版方法模式

作者 : 开心源码 本文共3993个字,预计阅读时间需要10分钟 发布时间: 2022-05-12 共224人阅读

模版方法模式

一、什么是模版方法模式

模板模式 :处理某类事情的步骤有些是固定的,有些是会发生变化的,那么这时候我们可以为这类事情提供一个模板代码,从而提高效率。

1.生活中的模版:

办理银行业务:

  1. 进门取号
  2. 填写单据(每个用户填写的单据都不一样,因业务不同而不同)
  3. 等待叫号
  4. 窗口办理

二、如何实现模版方法模式

1.模版方法模式的实现要素

准备一个笼统类,将部分逻辑以具体方法的形式实现,而后公告少量笼统方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性。最后将方法汇总构成一个不可改变的模版方法。

2.模版模式的步骤

  1. 先写出处理该类事情其中 的一件的处理方案。
  2. 分析代码,把会发生变化的代码抽取出来独立成一个方法。把该方法形容成一个笼统的方法。
  3. 使用final修饰模板方法,防止别人重写你的模板方法。

3.具体实现

案例一:编写一个计算程序运行时间的模板。

  • 记录开始时间
  • 代码的执行过程
  • 记录结束时间
  • 运行时间=结束时间-开始时间

笼统基类:

abstract class MyRuntime{        public final void getTime(){        long startTime = System.currentTimeMillis();    //记录开始的时间        code();        long endTime = System.currentTimeMillis();  //记录结束的时间.        System.out.println("运行时间 :"+ (endTime-startTime));    }    public abstract void code();}

具体子类:

class Demo extends MyRuntime{    public static void main(String[] args)     {        Demo d = new Demo();        d.getTime();    }    //code方法内部就写要计算运行时间的代码;    public  void code(){        int i = 0;        while(i<100){            System.out.println("i="+i);            i++;        }    }}

案例二:饮料的制法:

把水煮沸(boilWater)冲饮料(brew)把饮料倒进杯子(pourInCup)加调味料(addCondiments)

笼统基类:Drinks

package com.hcx.pattern.template;/** * 笼统基类,为所有子类提供一个算法框架 * 饮料 * @author HCX * */public abstract class Drinks {        /**     * 使用final修饰,防止子类改变模版方法     * 制备饮料的模版方法     * 封装了所有子类共同遵循的算法框架     */    public final void prepareDrinksTemplate(){        //步骤一:把水煮沸        boilWater();        //步骤二:冲饮料        brew();        //步骤三:把饮料倒进杯子        pourInCup();        //步骤四:加调味料        addCondiments();            }        /**     * 基本方法:把水煮沸     * 对所有子类,是一个共同的行为,不需要向子类开放;将变化的东西放在高层代码中。     */    private void boilWater() {        System.out.println("把水煮沸");    }    /**     * 基本方法:将饮料倒入杯中     */    private void pourInCup() {        System.out.println("将饮料倒入杯中");    }    /**     * 不同的情况,具体的实现不同,设计为笼统方法,需要在子类中可见,以便子类复写,提供具体的实现。     * 笼统的基本方法:加入调料     */    protected abstract void addCondiments();    /**     * 笼统的基本方法:泡饮料     */    protected abstract void brew();}

具体子类:Coffee

package com.hcx.pattern.template;/** * 具体子类,提供了咖啡制备的具体实现 * @author HCX * */public class Coffee extends Drinks {    @Override    protected void brew() {        System.out.println("用沸水冲泡咖啡");    }        @Override    protected void addCondiments() {        System.out.println("加入糖和牛奶");    }}

具体子类:OrangeJuice

package com.hcx.pattern.template;/** * 具体子类,提供了橙汁的具体实现 * @author HCX * */public class OrangeJuice extends Drinks{    @Override    protected void brew() {        System.out.println("准备橙子和榨汁机,把橙子丢入机器中榨汁");    }        @Override    protected void addCondiments() {        System.out.println("加入糖浆");    }}

测试:

package com.hcx.pattern.template;public class DrinksTest {        public static void main(String[] args) {        System.out.println("咖啡制备中");        Drinks drinks = new Coffee();        drinks.prepareDrinksTemplate();        System.out.println("咖啡好了");                System.out.println("*************************************");                System.out.println("橙汁制备中");        Drinks drinks2 = new OrangeJuice();        drinks2.prepareDrinksTemplate();        System.out.println("橙汁好了");    }}

结果:

咖啡制备中把水煮沸用沸水冲泡咖啡将饮料倒入杯中加入糖和牛奶咖啡好了*************************************橙汁制备中把水煮沸准备榨汁机和榨汁机,把橙子丢入机器中榨汁将饮料倒入杯中加入糖浆橙汁好了

使用钩子方法使代码更灵活:

在制备橙汁时,不想加入糖浆;

修改Drinks类,在加入调味料的步骤进行判断,编写钩子函数:

package com.hcx.pattern.template;/** * 笼统基类,为所有子类提供一个算法框架 * 饮料 * @author HCX * */public abstract class Drinks {        /**     * 使用final修饰,防止子类改变模版方法     * 制备饮料的模版方法     * 封装了所有子类共同遵循的算法框架     */    public final void prepareDrinksTemplate(){        //步骤一:把水煮沸        boilWater();        //步骤二:冲饮料        brew();        //步骤三:把饮料倒进杯子        pourInCup();                //步骤四:加调味料        if(wantCondiments()){            addCondiments();        }    }    /**     * Hook:钩子函数,提供一个默认或者空的实现     * 具体的子类可以自行决定能否挂钩以及如何挂钩,即能否重写父类的钩子函数     * 根据个人喜好,能否加入调料     * @return     */    protected boolean wantCondiments() {        return true;    }    /**     * 基本方法:把水煮沸     * 对所有子类,是一个共同的行为,不需要向子类开放;将变化的东西放在高层代码中。     */    private void boilWater() {        System.out.println("把水煮沸");    }    /**     * 基本方法:将饮料倒入杯中     */    private void pourInCup() {        System.out.println("将饮料倒入杯中");    }    /**     * 不同的情况,具体的实现不同,设计为笼统方法,需要在子类中可见,以便子类复写,提供具体的实现。     * 笼统的基本方法:加入调料     */    protected abstract void addCondiments();    /**     * 笼统的基本方法:泡饮料     */    protected abstract void brew();}

在OrangeJuice中重写钩子函数时:

package com.hcx.pattern.template;/** * 具体子类,提供了橙汁的具体实现 * @author HCX * */public class OrangeJuice extends Drinks{    @Override    protected void brew() {        System.out.println("准备橙子和榨汁机,把橙子丢入机器中榨汁");    }        @Override    protected void addCondiments() {        System.out.println("加入糖浆");    }        /**     * 重写父类的钩子方法     * 不加入任何调料,纯正的橙汁     */    @Override    protected boolean wantCondiments() {        return false;    }}

测试类打印结果:

咖啡制备中把水煮沸用沸水冲泡咖啡将饮料倒入杯中加入糖和牛奶咖啡好了*************************************橙汁制备中把水煮沸准备橙子和榨汁机,把橙子丢入机器中榨汁将饮料倒入杯中橙汁好了

总结:

模版模式总结.png

三、模版方法模式的适用场景及优缺点

适用场景:

  • 算法或者操作遵循类似的逻辑
  • 重构时(把相同的代码抽取到父类中)
  • 重要、复杂的算法,核心算法设计为模版算法

优点:

  • 封装性好
  • 复用性好
  • 屏蔽细节
  • 便于维护

缺点:

  • 单继承

四、实例分析

分析解决各种日志

需求分析:

日志需求分析.png

可笼统为如下的步骤:

  • 取得文件
  • 打开文件
  • 读取日志结构
  • 解决单行日志(会变化的部分,推迟到子类实现)
  • 清除工作

根据不同的情况,在变化部分的前后提供少量函数来提供扩展。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 模版方法模式

发表回复