首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式03——Template Method模式

设计模式03——Template Method模式

作者头像
itlemon
发布2020-04-03 16:17:33
7650
发布2020-04-03 16:17:33
举报
文章被收录于专栏:深入理解Java深入理解Java
定义

模板方法(Template Method)模式就是带有模板功能的模式 ,组成模板方法的方法被定义在父类中,这些方法是抽象方法,在模板方法中规定了这些方法的执行流程,这些抽象方法需要子类来具体实现。换句话说,模板方法就是定义好了模板,也就是一定的流程,至于各个抽象方法的具体实现,则有子类们自己决定,所以查看父类的代码是无法知晓这些方法最终会进行何种具体处理,唯一知道的就是父类是如何调用这些方法的。

实现上述这些抽象方法的是子类,在子类中实现了抽象方法也就决定了具体的处理。也就是说,不用的子类中的实现是不同的,当父类的模板方法被调用的时候,处理的方式也就不同,但是值得一提的是,无论子类如何实现抽象方法,如何自定义各自的处理逻辑,它调用父类的模板方法的时候,都会按照父类事先规定好的流程来分别调用这些方法。像这种在父类中定义好处理流程的框架,在子类中实现具体处理的模式就是模板方法(Template Method)模式。

问题引入

在生活中常常能见到类似模板方法模式的案例。比如,我们小时候都练过字帖,我们只要用笔就可以在字帖上临摹出优美的文字出来,看到字帖,在临摹之前就可以知道我们将会写出那些字出来,但是写出来字的效果就得依靠笔的类型,使用毛笔能临摹出粗字体,使用签字笔能临摹出细字体。

在比如,在炒菜的时候,一般步骤都是:往锅里倒油——打开天然气灶——加入具体蔬菜——加入具体调料——出锅,那么这个流程步骤就是一个模板,我们按照这个流程就可以炒出一盘热腾腾的蔬菜,至于加入的蔬菜和调料是什么类型,那么就得根据自己的口味了。

还有,一般我们玩游戏都有一个具体的步骤:初始化游戏——开始玩游戏——游戏结束,至于玩的是何种游戏,就可以根据自己的喜好来选择,但是都会遵循这个游戏步骤。这个步骤在模板方法模式中就是对应的模板。后面的示例代码将结合这个问题来对模板方法设计模式进行阐述。

模板方法设计模式在JDK源码中的应用

模板方法模式也是一个非常常用的设计模式之一,在JDK源码中,就存在大量的模板方法设计模式的身影,比如:

  • java.io.InputStream, java.io.OutputStream, java.io.Reader以及java.io.Writer中所有非抽象方法。
  • java.util.AbstractList, java.util.AbstractSet以及java.util.AbstractMap中所有非抽象方法。

接下来,我们一起来阅读java.io.InputStream的部分源码,来感受一下模板方法设计模式是如何在JDK中应用的。这里列举java.io.InputStream中一个抽象方法和一个非抽象方法,其中非抽象方法就是模板设计模式中的重要角色——模板。

public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

public abstract int read() throws IOException;

上面的非抽象方法给定了从输入流中读取数据的具体流程,而如何从输入流中读取,却没有给出具体的实现方法,需要java.io.InputStream这个抽象类的子类来具体实现read方法。

手动实现模板方法设计模式

也许阅读到这里,你对模板方法设计模式还没有一个清晰的认识,没关系,接下来将从最简单的示例开始,来展现模板方法设计模式的基本用法和原理。 我们选择上面问题引入中的“玩游戏”,使用代码来具体实现模板方法设计模式。首先,我们需要一个抽象类,这个抽象类有可变内容和不可变内容,可变内容就是该抽象类拥有抽象方法,这就需要子类去实现它,不同的子类对其实现方式往往是不同的;不可变内容就是该抽象类拥有非抽象方法,这个抽象方法往往是由final来修饰,它不允许子类来覆盖它,它就是模板方法,它规定了各个抽象方法的执行流程,也就是说,当子类来调用这个模板方法的时候,各个抽象方法实现方式虽然不同,但是他们的执行流程和顺序确实一致的。这个就是模板方法设计模式的基本原理。我们将模板方法设计模式类图设计如下:

在这里插入图片描述
在这里插入图片描述
  • 抽象类Game
package cn.itlemon.design.pattern.chapter03.template.method;

/**
 * 模板方法设计模式主要抽象类
 *
 * @author jiangpingping
 * @date 2018/9/14 下午3:22
 */
public abstract class Game {

    /**
     * 初始化游戏
     */
    public abstract void initialize();

    /**
     * 开始游戏
     */
    public abstract void startPlay();

    /**
     * 结束游戏
     */
    public abstract void endPlay();

    /**
     * 模板方法:确定了游戏的流程
     */
    public final void playGame() {
        initialize();
        startPlay();
        endPlay();
    }
}

注意观察这个抽象类,它有可变内容initializestartPlayendPlay三个抽象方法,有一个不可变内容playGame方法,其中不可变内容playGame方法规定了上面三个抽象方法的执行顺序。

  • 子类BasketBallGame
package cn.itlemon.design.pattern.chapter03.template.method;

/**
 * 篮球游戏?
 *
 * @author jiangpingping
 * @date 2018/9/14 下午3:27
 */
public class BasketBallGame extends Game {

    @Override
    public void initialize() {
        System.out.println("Basketball Game Initialized! Start playing.");
    }

    @Override
    public void startPlay() {
        System.out.println("Basketball Game Started. Enjoy the game!");
    }

    @Override
    public void endPlay() {
        System.out.println("Basketball Game Finished!");
    }
}
  • 子类FootBallGame
package cn.itlemon.design.pattern.chapter03.template.method;

/**
 * 足球游戏⚽️
 *
 * @author jiangpingping
 * @date 2018/9/14 下午3:30
 */
public class FootBallGame extends Game {

    @Override
    public void initialize() {
        System.out.println("Football Game Initialized! Start playing.");
    }

    @Override
    public void startPlay() {
        System.out.println("Football Game Started. Enjoy the game!");
    }

    @Override
    public void endPlay() {
        System.out.println("Football Game Finished!");
    }
}

这两个子类都继承了Game这抽象类,并且重写了三个抽象方法,这正印证了模板方法设计模式的定义:在父类中定义好处理流程的框架,在子类中实现具体处理

  • 测试类Main
package cn.itlemon.design.pattern.chapter03.template.method;

/**
 * @author jiangpingping
 * @date 2018/9/14 下午3:32
 */
public class Main {

    public static void main(String[] args) {
        Game basketBallGame = new BasketBallGame();
        Game footBallGame = new FootBallGame();
        basketBallGame.playGame();
        System.out.println();
        footBallGame.playGame();
    }
}

在该测试类中,我们分别创建了两个子类的对象,并将这两个对象保存在父类的变量中,当分别调用playGame方法的时候,和我们预计想一致,按照指定的顺序将三个可变方法进行了调用,但不同子类的具体实现是不一样的。

Basketball Game Initialized! Start playing.
Basketball Game Started. Enjoy the game!
Basketball Game Finished!

Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!
浅析模板方法模式中的重要角色

在模板方法设计模式中,主要角色只有两个,分别是:描述抽象方法和模板方法的抽象类,以及实现抽象方法的具体子类。

  • AbstractClass(抽象类)

AbstractClass角色主要负责实现模板方法,并且还负责声明模板方法中用到的抽象方法,这些抽象方法由具体的子类来进行实现,模板方法负责规定这些抽象方法的调用顺序。在本次示例中,该角色由Game来扮演。

  • ConcreteClass(具体类)

ConcreteClass角色主要负责实现AbstractClass角色中声明的抽象方法,不同的ConcreteClass对这些抽象方法实现的方式不一样的,但是由于在父类中规定了这些抽象方法的调用顺序,所有,即使具体的实现方式不一样,但是最终的执行顺序都是一致的。

模板方法模式UML类图

模板方法设计模式UML类图如下所示:

在这里插入图片描述
在这里插入图片描述
为什么要使用模板方法模式

究竟使用模板方法模式可以给我们的代码带来什么好处呢?它的主要优点就是在父类中编写好了算法,在子类中无需重复编写,如果算法有问题,那么只需要修改父类中模板方法即可。 还有重要的一点就是,在使用父类类型变量保存子类实例对象的时候,无需使用instanceof等指定子类的具体类型,也可以直接调用模板方法。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-09-25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定义
  • 问题引入
  • 模板方法设计模式在JDK源码中的应用
  • 手动实现模板方法设计模式
  • 浅析模板方法模式中的重要角色
  • 模板方法模式UML类图
  • 为什么要使用模板方法模式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档