设计模式-模板方法模式

模板方法模式的实现要素

  • 抽象基类

    • 提供基本的实现方法,具有共性的方法
    • 抽象方法,不知道具体实现的方法,由子类进行具体实现
    • 可选钩子函数,基类中提供默认或空实现
    • Template方法(final),将所有方法汇总而成的模板方法

    好莱坞原则:子类可以替换掉父类可变逻辑,但不能改变整体逻辑

  • 具体子类

    • 实现基类中的抽象方法,提供具体的,个性化的实现
    • 可选的覆盖钩子函数,个性化的影响局部行为
  • 总结

    • 准备一个抽象类,将部分逻辑以具体方法的形式实现,然后声明一些抽象方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性。最后将方法汇总构成一个不可改变的模板方法。

模板方法模式的适用场景

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

模板方法模式的优缺点

优点

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

缺点

  • 由于java语言单继承的特性,一个类已经继承了某一个类,那么我们无法在使用继承的方式实现模板方法模式

模板方法模式的基本实现

饮料调制的小例子

  • 基本过程
咖啡
把水煮沸把水煮沸
用沸水冲泡咖啡用沸水浸泡茶叶
把咖啡倒进杯子把咖啡倒进杯子
加糖和牛奶加柠檬
  • 抽取相似过程
把水煮沸(boilWater)
泡饮料(brew)
把饮料倒进杯子(pourInCup)
加调味料(addCondiments)

RefreshBeverage抽象基类

package com.example.java_study.template_method;

/**
 * @author xianping
 * @version 1.0
 * @classname RefreshBeverage[提神饮料]
 * @description 抽象基类,为所有子类提供算法框架
 * @date 2020/09/12 8:48
 */
public abstract class RefreshBeverage {
    /*
     * 制作饮料的模板方法
     * 封装了所有子类共同遵循的算法框架
     * @description [抽象基类定义了算法框架,final关键字禁止子类对算法框架做任何改变、阻止子类对父类方法复写]
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    public final void prepareBeverageTemplate() {
        //步骤1:将水煮沸
        boilWater();
        //步骤2:泡制饮料
        brew();
        //步骤3:将饮料倒入杯中
        pourInCup();
        //步骤4:加入调味料
        addCondiMents();
    }

    /*
     * @description 将水煮沸[公用方法,声明为private,减少子类复写工作量]
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    private void boilWater() {
        System.out.println("将水煮沸");
    }

    /*
     * @description 泡制饮料[需要子类进行实现的方法定义为abstract,具体实现由子类进行]
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    abstract void brew();

    /*
     * @description 将饮料倒入杯中[公用方法,声明为private,减少子类复写工作量]
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    private void pourInCup() {
        System.out.println("将饮料倒入杯中");
    }

    /*
     * @description 加入调味料[需要子类进行实现的方法定义为abstract,具体实现由子类进行]
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    abstract void addCondiMents();
}

Coffee实现类

package com.example.java_study.template_method;

/**
 * @author xianping
 * @version 1.0
 * @classname Coffee 具体子类、咖啡类
 * @description
 * @date 2020/09/12 9:10
 */
public class Coffee extends RefreshBeverage {

    /*
     * @description 将饮料倒入杯中
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    @Override
    void brew() {
        System.out.println("用沸水冲泡咖啡");
    }

    /*
     * @description 加入调味料
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    @Override
    void addCondiMents() {
        System.out.println("加入糖和牛奶");
    }
}

Tea实现类

package com.example.java_study.template_method;

/**
 * @author xianping
 * @version 1.0
 * @classname Coffee 具体子类、咖啡类
 * @description
 * @date 2020/09/12 9:10
 */
public class Tea extends RefreshBeverage {

    /*
     * @description 将饮料倒入杯中
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    @Override
    void brew() {
        System.out.println("用80度热水浸泡茶叶5分钟");
    }

    /*
     * @description 加入调味料
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    @Override
    void addCondiMents() {
        System.out.println("加入柠檬");
    }
}

测试方法

package com.example.java_study.template_method;

/**
 * @author xianping
 * @version 1.0
 * @classname RefreshBeverageTest
 * @description
 * @date 2020/09/12 9:14
 */
public class RefreshBeverageTest {
    public static void main(String[] args) {
        //制作咖啡
        RefreshBeverage coffee = new Coffee();
        coffee.prepareBeverageTemplate();
        //制作茶
        RefreshBeverage tea = new Tea();
        tea.prepareBeverageTemplate();
    }
}

结果

将水煮沸
用沸水冲泡咖啡
将饮料倒入杯中
加入糖和牛奶
将水煮沸
用80度热水浸泡茶叶5分钟
将饮料倒入杯中
加入柠檬

钩子函数

  • 如果我们想制作一杯不加任何调料的饮料,那么现有的模板是无法满足的
  • 这时可以引入一个钩子函数,子类可选择重写父类钩子函数从而达到制作无任何调味料的饮料

RefreshBeverage抽象基类

package com.example.java_study.template_method;

/**
 * @author xianping
 * @version 1.0
 * @classname RefreshBeverage[提神饮料]
 * @description 抽象基类,为所有子类提供算法框架
 * @date 2020/09/12 8:48
 */
public abstract class RefreshBeverage {
    /*
     * 制作饮料的模板方法
     * 封装了所有子类共同遵循的算法框架
     * @description [抽象基类定义了算法框架,final关键字禁止子类对算法框架做任何改变、阻止子类对父类方法复写]
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    public final void prepareBeverageTemplate() {
        //步骤1:将水煮沸
        boilWater();
        //步骤2:泡制饮料
        brew();
        //步骤3:将饮料倒入杯中
        pourInCup();
        //步骤4:加入调味料
        if (isCustomerWantsCondiments()) {
            addCondiMents();
        }
    }

    /*
     *Hook方法,钩子函数。提供一个默认实现或为空的实现,具体的子类可以自行决定是否挂钩以及如何挂钩
     * @description 询问用户是否加入调料[protected子类可以进行重写,非子类无法进行重写]
     * @author xianping
     * @date 2020/9/12
     * @return boolean
     **/
    protected boolean isCustomerWantsCondiments() {
        return true;
    }

    /*
     * @description 将水煮沸[公用方法,声明为private,减少子类复写工作量]
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    private void boilWater() {
        System.out.println("将水煮沸");
    }

    /*
     * @description 泡制饮料[需要子类进行实现的方法定义为abstract,具体实现由子类进行]
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    abstract void brew();

    /*
     * @description 将饮料倒入杯中[公用方法,声明为private,减少子类复写工作量]
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    private void pourInCup() {
        System.out.println("将饮料倒入杯中");
    }

    /*
     * @description 加入调味料[需要子类进行实现的方法定义为abstract,具体实现由子类进行]
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    abstract void addCondiMents();
}

Tea实现类

package com.example.java_study.template_method;

/**
 * @author xianping
 * @version 1.0
 * @classname Coffee 具体子类、咖啡类
 * @description
 * @date 2020/09/12 9:10
 */
public class Tea extends RefreshBeverage {

    /*
     * @description 将饮料倒入杯中
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    @Override
    void brew() {
        System.out.println("用80度热水浸泡茶叶5分钟");
    }

    /*
     * @description 加入调味料
     * @author xianping
     * @date 2020/9/12
     * @return void
     **/
    @Override
    void addCondiMents() {
        System.out.println("加入柠檬");
    }

    /*
     * @description 子类选择挂载钩子函数,通过重写的父类钩子函数,提供一个符合自己的实现
     * @author xianping
     * @date 2020/9/12
     * @return boolean
     **/
    @Override
    protected boolean isCustomerWantsCondiments() {
        return false;
    }
}

RefreshBeverageTest

package com.example.java_study.template_method;

/**
 * @author xianping
 * @version 1.0
 * @classname RefreshBeverageTest
 * @description
 * @date 2020/09/12 9:14
 */
public class RefreshBeverageTest {
    public static void main(String[] args) {
        //制作茶
        RefreshBeverage tea = new Tea();
        tea.prepareBeverageTemplate();
    }
}

结果

将水煮沸
用80度热水浸泡茶叶5分钟
将饮料倒入杯中
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值