努力保头发的打工人
努力保头发的打工人
发布于 2021-01-24 / 25 阅读 / 0 评论 / 0 点赞

模版方法模式

1 概述

1.1定义

定义一个操作中的算法的框架,而将一些步骤延伸到子类中。使得子类可以不改变算法结构即可重新定义该算法的某些特定步骤。

类图如下:

模板方法模式类图.png

模板抽象类中定义基本操作,由子类实现,然后在模板方法中被调用,实现对基本方法的调度,完成固定的逻辑。一般基本方法都由protected修饰,尽量不扩大父类中的访问权限。模板方法不可修改,尽量使用final修饰。完整模板方法模式还应该加入一个钩子函数,可以由子类决定部分执行结果。

1.2 优缺点

优点:封装不变部分,拓展可变部分;提取公共代码方便维护;行为由父类控制,子类实现。

缺点:子类执行结果影响了父类执行结果,也就是子类对父类产生了影响,在复杂的项目中,增加代码阅读的难度。

1.3 使用场景
  • 多个子类有共有的方法,并且实现逻辑基本相同时。

  • 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能由子类实现。

  • 重构时,把相同代码抽取到父类中,然后通过钩子函数约束其行为。

2 代码示例

/**
 * @author chiangtaol
 * @date 2020-11-21
 * @describe 对战游戏抽象类
 */
public abstract class AbstractPlayGame {
​
    /**
     * 匹配玩家
     */
    protected abstract void matchPlayers();
​
    /**
     * 选择英雄
     */
    protected abstract void chooseHeroes();
​
    /**
     * 选择皮肤
     */
    protected abstract void chooseSkin();
​
    /**
     * 开始对战
     */
    protected abstract void begin();
​
    /**
     * 结束对局
     */
    protected abstract void end();
​
    /**
     * 是否有钱
     *
     * @return /
     */
    protected boolean isRich(){
        return false;
    }
​
    final public void play() {
        this.matchPlayers();
        this.chooseHeroes();
        // 有钱可以选皮肤,没钱老老实实用默认皮肤
        if (this.isRich()) {
            this.chooseSkin();
        }
        this.begin();
        this.end();
    }
​
}
/**
 * @author chiangtaol
 * @date 2020-11-21
 * @describe 英雄联盟游戏
 */
public class LeagueOfLegends extends AbstractPlayGame {
​
    protected boolean rich = false;
​
    @Override
    public boolean isRich() {
        return this.rich;
    }
​
    public void setRich(boolean rich) {
        this.rich = rich;
    }
​
    /**
     * 匹配玩家
     */
    @Override
    protected void matchPlayers() {
        System.out.println("匹配到10个人...");
    }
​
    /**
     * 选择英雄
     */
    @Override
    protected void chooseHeroes() {
        System.out.println("选择操作英雄");
    }
​
    /**
     * 选择皮肤
     */
    @Override
    protected void chooseSkin() {
        System.out.println("选择终极皮肤");
    }
​
    /**
     * 开始对战
     */
    @Override
    protected void begin() {
        System.out.println("全军出击...");
    }
​
    /**
     * 结束对局
     */
    @Override
    protected void end() {
        System.out.println("游戏结束");
    }
​
}

下面测试一下,根据钩子方法去改变执行结果

/**
 * @author chiangtaol
 * @date 2020-11-24
 * @describe
 */
class AbstractPlayGameTest {
​
    @Test
    void play() throws Exception{
        LeagueOfLegends playGame = new LeagueOfLegends();
        //playGame.setRich(true);
        playGame.play();
    }
}
​
//匹配到10个人...
//选择操作英雄
//全军出击...
//游戏结束
​
//匹配到10个人...
//选择操作英雄
//选择终极皮肤
//全军出击...
//游戏结束