设计模式--模板方法

1、什么是模板方法模式

举例:生活的模板

去银行办理业务的通用过程,
Step1:进门取号
Step2:填写单据
Step3:等待叫号
Step4:窗口办理

这个过程不会因为是“你”或者是“我”有什么不同,银行为所有用户定制了这么一个模板,但是Step2中银行不知道每个客户的每笔业务,在这定制了这个步骤,留给每个客户具体实现。

接下来看一下模板方法模式是如何定义的?

首先模板方法模式为我们定义了一个操作中的算法骨架,骨架中定义了一些算法执行的步骤,但是框架中有一些步骤延迟到子类实现,如Step2。使得子类在不改变算法结构的同时,就重新定义该算法的某些特点步骤,这就是模板方法模式。

2、如何实现模板方法模式

以饮料的调制方法为例,

咖啡的泡法:
(1)把水煮沸
(2)用沸水冲泡咖啡
(3)把咖啡倒进杯子
(4)加糖和牛奶
茶的泡法:
(1)把水煮沸
(2)用沸水浸泡茶叶
(3)把茶倒进杯子
(4)加柠檬

观察这两种饮料的制备方法,发现有一些共性,比如把水煮沸,倒入杯子。而另外两个步骤体现出来不同,但往前看,不管是冲泡还是浸泡都是泡,不管加牛奶还是加柠檬都是加了一些调料。
基于这个认识上,认为提神饮料的泡法,可以分为一下步骤:
(1)把水煮沸(boilWater)
(2)泡饮料(brew)
(3)把饮料倒进杯子(pourInCup)
(4)加入调料(addCondiments)
代码实现:

基本实现:
(1)创建算法基类RefreshBeverage:

首先要先定一个共同遵循的模板方法。然后在模板方法中定义共有的方法。接下来在基本方法中,如boilWater()、pourInCup(),它们是对所有子类而言共同的方法,所以没有必要在对子类做过多的开放,可以生命为私有的方法,这样在子类编码的时候,减少我们的复杂度,这就是模板方法的好处。另外两个方法的共同特点是我们在算法框架中并不知道具体实现是什么样子的,所以应该做成抽象方法,并且由于需要在子类中可见,便于复写,所以声明为protected权限。

/**
 * 抽象基类,为所有子类提供一个算法框架
 *
 *
 * 提神饮料
 */

public abstract class RefreshBeverage {

    /**
     * 制备饮料的模板方法‘
     * 封装了所有子类共同遵循的算法框架
     */
    public final void prepareBeverageTemplate(){
        //步骤1:将水煮沸
        boilWater();

        //步骤2:泡制饮料
        brew();

        //步骤3:将饮料倒入杯中
        pourInCup();

        //步骤4:加入调味料
        addCondiments();
    }

    /**
     * 抽象的基本方法:加入调料
     */
    protected abstract void addCondiments();

    /**
     * 基本方法:将饮料倒入杯中
     */
    private void pourInCup() {
        System.out.println("将饮料倒入杯中");
    }

    /**
     * 抽象的基本方法:泡制饮料
     */
    protected abstract void brew();

    /**
     * 基本方法:将水煮沸
     */
    private void boilWater(){
        System.out.println("将水煮沸");
    }
}

注意点:

① prepareBeverageTemplate一定要用final来修饰,因为模板方法模式使得抽象基类来定义了算法框架,而禁止子类对算法框架做任何的改变。所以使用final来阻止子类对模板方法的复写。

(2)创建子类Coffee:
重写所有基类中的抽象方法,实现延迟步骤。

/**
 * 具体子类,提供了咖啡制备的具体实现
 */

public class Coffee extends RefreshBeverage {
    @Override
    protected void addCondiments() {
        System.out.println("加入糖和牛奶");
    }

    @Override
    protected void brew() {
        System.out.println("用沸水冲泡咖啡");
    }
}

(3)创建子类Tea:

/**
 * 具体子类,提供了制备茶的具体实现
 */

public class Tea extends RefreshBeverage {
    @Override
    protected void addCondiments() {
        System.out.println("加入柠檬");
    }

    @Override
    protected void brew() {
        System.out.println("用80度的热水浸泡茶叶5分钟");
    }
}

(4)测试类的实现

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        System.out.println("制备咖啡");
        RefreshBeverage b1 = new Coffee();
        b1.prepareBeverageTemplate();
        System.out.println("咖啡好了");

        System.out.println("/********************************************/");

        System.out.println("制备茶");
        RefreshBeverage b2 = new Tea();
        b2.prepareBeverageTemplate();
        System.out.println("茶好了");
    }
}

(5)打印结果
这里写图片描述

改进实现:

比如有的朋友想喝一杯不加牛奶和糖的咖啡,或者有的朋友想喝一杯绿茶,这又该怎么实现呢?上面步骤4的实现是不是规定的太死,任何饮料都得加入调味品,怎么做一些个性化扩展呢?这里引入钩子方法概念。
当做一杯绿茶时的代码修改:
(1)首先将RefreshBeverage类修改如下:
在调用步骤4时加入判断,isCustomWantsCondiments()这个方法就是钩子方法,提供一个默认或空的实现,具体的子类可以自行决定是否挂钩以及如何挂钩。

/**
 * 抽象基类,为所有子类提供一个算法框架
 *
 *
 * 提神饮料
 */

public abstract class RefreshBeverage {

    /**
     * 制备饮料的模板方法‘
     * 封装了所有子类共同遵循的算法框架
     */
    public final void prepareBeverageTemplate(){
        //步骤1:将水煮沸
        boilWater();

        //步骤2:泡制饮料
        brew();

        //步骤3:将饮料倒入杯中
        pourInCup();

        if (isCustomWantsCondiments()){
            //步骤4:加入调味料
            addCondiments();
        }
    }

    /**
     * Hook,钩子函数,提供一个默认或空的实现
     * 具体的子类可以自行决定是否挂钩以及如何挂钩
     * 询问用户是否加入调料
     * @return
     */
    protected boolean isCustomWantsCondiments() {
        return true;
    }

    /**
     * 抽象的基本方法:加入调料
     */
    protected abstract void addCondiments();

    /**
     * 基本方法:将饮料倒入杯中
     */
    private void pourInCup() {
        System.out.println("将饮料倒入杯中");
    }

    /**
     * 抽象的基本方法:泡制饮料
     */
    protected abstract void brew();

    /**
     * 基本方法:将水煮沸
     */
    private void boilWater(){
        System.out.println("将水煮沸");
    }
}

(2)然后修改子类Tea:
覆盖父类的钩子函数

/**
 * 具体子类,提供了制备茶的具体实现
 */

public class Tea extends RefreshBeverage {
    @Override
    protected void addCondiments() {
        System.out.println("加入柠檬");
    }

    @Override
    protected void brew() {
        System.out.println("用80度的热水浸泡茶叶5分钟");
    }

    /**
     * 子类通过覆盖的形式选择挂载钩子函数
     * @return
     */
    @Override
    protected boolean isCustomWantsCondiments() {
        return false;
    }
}

(3)打印结果
这里写图片描述

3、模板方法模式的特点
(1)模板方法模式实现要素:
① 抽象基类
包括基本方法(这种方法对不同实现子类而言是相同的,具有共性的)和抽象方法(只知道具体原则,不知道实现细节,需要延长到子类实现),还有钩子方法。然后将基本方法和抽象方法汇总而成一个模板方法(final修饰)
② 具体子类
实现基类中的抽象方法和覆盖钩子方法。
总结:准备一个抽象类,将部分逻辑以具体方法的形式实现,然后声明一些抽象方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性,最后将方法汇总构成一个不可改变的模板方法。
(2)适用场景:
① 算法或操作遵循相似的逻辑。
② 重构时(把相同的代码抽取到父类中)。
③ 重要、复杂的算法,核心算法设计为模板算法。
(3)优点:
① 封装性好
② 复用性好
③ 屏蔽细节
④ 便于维护
(4)缺点:
继承:单继承的缺点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
模板方法设计模式是一种行为设计模式,它定义了一个算法的骨架,将一些步骤的实现延迟到子类中。这种模式可以确保算法的结构保持不变,但允许子类提供特定的实现细节。在C++中,模板方法设计模式可以通过使用虚函数和继承来实现。 在给出的引用中,我们可以看到一个名为CoffeineBeverage的类,它定义了一个prepare_recipe()方法,并在内部调用了其他几个私有方法。这个类是一个基类,可以被子类继承并重写其中的方法。 具体来说,模板方法设计模式的关键是将算法的骨架定义在基类中,而将可变的实现细节留给子类去实现。在这个例子中,prepare_recipe()方法是算法的骨架,而brew()和add_condiments()方法则是可变的实现细节。 通过将brew()和add_condiments()方法定义为虚函数,基类CoffeineBeverage允许子类去重写这些方法以提供自己的实现。这样,当调用prepare_recipe()方法时,实际执行的是子类中重写后的方法。 使用模板方法设计模式的好处是可以提高代码的复用性和可扩展性。算法的骨架在基类中只需要定义一次,而具体的实现细节可以在不同的子类中灵活变化。 总结起来,C++中的模板方法设计模式通过定义一个算法的骨架并将可变的实现细节留给子类去实现,可以提供代码的复用性和可扩展性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [C++设计模式模板方法模式](https://blog.csdn.net/Long_xu/article/details/127118813)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [C++设计模式-模板方法模式](https://blog.csdn.net/qq78442761/article/details/91990149)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值