1.定义

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

模板方法模式是一种行为设计模式, 它在父类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。主要要用来复用代码

2.场景

一般用在步骤相同,但是逻辑不同的场景。比如,你现在要去公司,从家里出发,然后到公司。这个中间过程你可以选择步行,骑车,或者开车,地铁,公交等等。其步骤就是: 家->出行方式->公司,最终达到的效果是一样的,但是具体的出行方式不同。

3.案例模拟

1.场景描述

还是用英雄联盟里面的场景来描述,本来是想通过云顶场景来作为案例的,后面看都有类似的文章,而且更加的合适所以就引用了。在游戏开始之前,玩家们都要经过选择英雄,选择符文,选择召唤师技能这写过程。可以把这些步骤定义成一个模板,然后里面特定步骤由子类自己定制化。

如下图类似:

2.创建模板

定义游戏模板:

public abstract class NormalBlindPickProcessAbstract {

    //英雄选择列表
    protected List<String> heroList;

    private Map<String,Object> environment = new HashMap<>();

    public void gameStart() {

        //初始化游戏环境
        prepareEnvironment();
        //初始化英雄列表
        initHeroList();
        //选择英雄
        pickHero();
        //选择符文
        chooseTalents();
        //选择召唤师技能
        chooseBaseSkill();
        //启动游戏
        System.out.println("游戏开始!");
    }


    protected abstract void pickHero();
    protected abstract void chooseTalents();
    protected abstract void chooseBaseSkill();

    /**
     * 模拟游戏环境
     */
    private final void prepareEnvironment (){
        //游戏唯一id
        String gameId = UUID.randomUUID().toString();
        //玩家列表
        List<String> players = new ArrayList<>();
        environment.put("gemaId",gameId);
        environment.put("players",players);
    }

    /**
     * 初始化英雄列表
     */
    private final void initHeroList (){
        //模拟英雄列表算法
        heroList = new ArrayList<>();
        heroList.add("Ez");
        heroList.add("Vn");
        heroList.add("GayLun");
    }
}

定义玩家A:

public class PlayerA extends NormalBlindPickProcessAbstract{

    private String hero;

    @Override
    protected void pickHero() {
        //模拟选择英雄
        this.hero = heroList.get(0);
        System.out.println("玩家A选择英雄:"+hero);
    }

    @Override
    protected void chooseTalents() {
        System.out.println("玩家A选择主宰符文!");
    }

    @Override
    protected void chooseBaseSkill() {
        System.out.println("玩家A选择闪现和和点燃");
    }
}

定义玩家B:

public class PlayerB extends NormalBlindPickProcessAbstract{

    private String hero;

    @Override
    protected void pickHero() {
        //选择Ez
        this.hero = heroList.get(2);
        System.out.println("玩家B选择英雄:"+hero);
    }

    @Override
    protected void chooseTalents() {
        System.out.println("玩家B选择精密符文!");
    }

    @Override
    protected void chooseBaseSkill() {
        System.out.println("玩家B选择治疗和闪现");
    }
}

3.测试

public class TestApi {

    public static void main(String[] args) {
        NormalBlindPickProcessAbstract playerA = new PlayerA();
        playerA.gameStart();
        NormalBlindPickProcessAbstract playerB = new PlayerB();
        playerB.gameStart();
    }

}

//结果
玩家A选择英雄:Ez
玩家A选择主宰符文!
玩家A选择闪现和和点燃
游戏开始!
玩家B选择英雄:GayLun
玩家B选择精密符文!
玩家B选择治疗和闪现
游戏开始!

上面案例只要是复用了代码,避免每个类都去实现一边相同的逻辑。而且子类只用关心自己的逻辑就可以。

4.拓展

1.callBack函数

模板模式和callBack这个概念有点类似,都有钩子函数的味道。预先提供一个函数,然后交给子类去执行。

而callBack的概念是基于组合模式的。

2.callBack案例

public interface ICallback {
  void methodToCallback();
}

public class BClass {
  public void process(ICallback callback) {
    //...
    callback.methodToCallback();
    //...
  }
}

public class AClass {
  public static void main(String[] args) {
    BClass b = new BClass();
    b.process(new ICallback() { //回调对象
      @Override
      public void methodToCallback() {
        System.out.println("Call back me.");
      }
    });
  }
}

参考文章:

模板方法设计模式

Template method

重学 Java 设计模式:实战模版模式「模拟爬虫各类电商商品,生成营销推广海报场景」 | bugstack 虫洞栈

Java设计模式之模板模式【通过LOL选英雄案例】_zxl_LangYa的博客-CSDN博客

59 | 模板模式(下):模板模式与Callback回调函数有何区别和联系?-极客时间

更多推荐

设计模式之模板模式