【设计模式】Java设计模式之模板方法模式

简介: 转载请注明出处:http://chenhaoxiang.cn本文源自【人生之旅_谙忆的博客】模板方法模式初探我们先这样来想象一个生活中的场景, 就是我们在银行柜台进行办理业务的时候,会进行这几步: Step1:进门取号 Step2:填写单据 Step3:等待叫号 Step4:窗口办理 在这里,无论是你我还是他/她,都会遵循这个模板进行业务的办理!我们具体看Step2,银行是无法知道我们的信息的,所以无法由银行具体实现了,所以留下接口,需要我们自己去实现。

转载请注明出处:http://chenhaoxiang.cn

本文源自人生之旅_谙忆的博客

模板方法模式初探

我们先这样来想象一个生活中的场景,
就是我们在银行柜台进行办理业务的时候,会进行这几步:
Step1:进门取号
Step2:填写单据
Step3:等待叫号
Step4:窗口办理

在这里,无论是你我还是他/她,都会遵循这个模板进行业务的办理!

我们具体看Step2,银行是无法知道我们的信息的,所以无法由银行具体实现了,所以留下接口,需要我们自己去实现。

现在我们再继续看下去


什么是模板方法

模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。
比如定义一个操作中的算法的骨架,将步骤延迟到子类中。模板方法使得子类能够不去改变一个算法的结构即可重定义算法的某些特定步骤。

前面那个例子可以这样用图来描述:


简单明了,就是Step2自己去具体实现。


模板方法模式的代码实现

具体子类实现延迟步骤


package com.chenhaoxiang.template;

/**
 * 抽象基类 为所有子类提供一个算法框架
 * 
 * 提神饮料
 * 
 * @author chenhaoxiang
 *
 */
public abstract class RefreshBeverage {
    /**
     * final阻止子类对方法的复写 制备饮料的模板方法 封装了所有子类共同遵循的算法框架
     */
    public final void prepareBeverageTemplate() {
        // 步骤1:将水煮沸
        boilWater();
        // 步骤2:泡制饮料
        brew();
        // 步骤3:将饮料倒入杯中
        pourInCup();
        // 步骤4: 加入调味料
        addCondiments();
    }

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


    /**
     * 抽象的基本方法
     * 泡制饮料
     * 注意访问权限为protected
     */
    protected abstract void brew();

    /**
     * 通用方法
     * 将饮料倒入杯中
     */
    private void pourInCup() {
        System.out.println("将饮料倒入杯子中");
    }
    /**
     * 抽象的基本方法
     * 加入调味料
     */
    protected abstract void addCondiments();

}


package com.chenhaoxiang.template;

/**
 * 具体子类
 * 提供了咖啡制备的具体实现
 * 
 * @author chenhaoxiang
 *
 */
public class Coffee extends RefreshBeverage{

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

    @Override
    protected void addCondiments() {
        System.out.println("加入咖啡调料...");
    }

}
package com.chenhaoxiang.template;

/**
 * 具体子类
 * 提供了制备茶的具体实现
 * @author chenhaoxiang
 *
 */
public class Tea extends RefreshBeverage{

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

    @Override
    protected void addCondiments() {
        System.out.println("茶也要调味品?反正我不加.");
    }

}


package com.chenhaoxiang.template;

public class RefreshBeverageTest {

    public static void main(String[] args) {

        System.out.println("制备咖啡...");

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

        System.out.println("---------------------");

        System.out.println("开始制备茶.");
        RefreshBeverage beverage2 = new Tea();
        beverage2.prepareBeverageTemplate();
        System.out.println("茶制作成功.");



    }
}

结果:

钩子使子类更灵活

看上面代码实现的步骤:
1、把水煮沸
2、泡饮料
3、把饮料倒入杯子
4、加调味品

上面的代码中,我们的子类是必须进行上面的4步的,因为那是在我们的模板方法中定义的。
如果我们不需要加调味品呢,这个时候就需要引入钩子方法的概念了。

对上面的代码进行变化

未写的是未进行修改的类


package com.chenhaoxiang.template;

/**
 * 抽象基类 为所有子类提供一个算法框架
 * 
 * 提神饮料
 * 
 * @author chenhaoxiang
 *
 */
public abstract class RefreshBeverage {
    /**
     * final阻止子类对方法的复写 制备饮料的模板方法 封装了所有子类共同遵循的算法框架
     */
    public final void prepareBeverageTemplate() {
        // 步骤1:将水煮沸
        boilWater();
        // 步骤2:泡制饮料
        brew();
        // 步骤3:将饮料倒入杯中
        pourInCup();
        if( isCustomerWantsCondiments() ){
            // 步骤4: 加入调味料
            addCondiments();
        }
    }

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

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


    /**
     * 抽象的基本方法
     * 泡制饮料
     * 注意访问权限为protected
     */
    protected abstract void brew();

    /**
     * 通用方法
     * 将饮料倒入杯中
     */
    private void pourInCup() {
        System.out.println("将饮料倒入杯子中");
    }
    /**
     * 抽象的基本方法
     * 加入调味料
     */
    protected abstract void addCondiments();

}


package com.chenhaoxiang.template;

/**
 * 具体子类
 * 提供了制备茶的具体实现
 * @author chenhaoxiang
 *
 */
public class Tea extends RefreshBeverage{

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

    @Override
    protected void addCondiments() {
        System.out.println("茶也要调味品?反正我不加.");
    }

    /**
     * 子类通过覆盖的形式选择挂载钩子函数并且提供了一个符合自己需求的实现
     */
    @Override
    protected boolean isCustomerWantsCondiments() {
        return false;//返回false ,addCondiments方法不会执行
    }

}

演示结果:

总结

抽象基类:
1、基本方法
我们知道所有的子类的实现细节都是一样的,具有共性的
可以直接在基类中定义实现
2、抽象方法
对于只知道原则而不知道细节的方法
3、可选钩子函数
只在基类中提供默认或者空的实现,由子类来选择是否使用钩子
4、Template方法
把前面的方法按照一定顺序来执行,写成模板方法。
注意,该方法一定要写成final!

也就是,你不能改变基类规定的基本原则,执行顺序!

具体子类:
1、实现基类中的抽象方法
执行子类的个性化行为
2、可选择覆盖钩子方法
可更加个性化的来影响局部行为

模板方法的适用场景:
1、算法或操作遵循相似的逻辑
例如上面代码样例中的茶和咖啡,对饮料的泡制具有相似的逻辑,所以可以抽象成模板方法,供所有子类使用!
2、重构时(把相同的代码抽取到父类中)
新功能加入时
3、重要、复杂的算法,核心算法设计为模板算法

模板方法模式的优点:
1、封装性好
2、复用性好
3、屏蔽细节
4、便于维护
优点具体就不说了,百度一下有很多解释。
模板方法模式的缺点:
1、继承
继承是面向对象的语言的一个核心的特性!
Java是单继承语言,也就是一个类只能有一个父类!
这种情况下,设想一个情况,在已有的历史系统中,这个系统的类有大量继承,如果我们想做一些重构,用模板方法的模式抽取共性,以及增加架构的弹性的时候,因为我们的类已经处于继承的某个继承的结构之中,如果想引用模板方法,可能就会遇到一些问题!

本文章由[谙忆]编写, 所有权利保留。
欢迎转载,分享是进步的源泉。

转载请注明出处:http://chenhaoxiang.cn

本文源自人生之旅_谙忆的博客

目录
相关文章
|
1月前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
1月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
39 4
|
2月前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
54 0
[Java]23种设计模式
|
1月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
2月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
2月前
|
设计模式 Java
Java设计模式
Java设计模式
38 0
|
2月前
|
设计模式 Java
Java设计模式之外观模式
这篇文章详细解释了Java设计模式之外观模式的原理及其应用场景,并通过具体代码示例展示了如何通过外观模式简化子系统的使用。
35 0
|
5天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
7天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。