模板方法模式详解

文章目录

  • 模板方法模式(行为模式)
    • 1. 模板方法模式介绍
    • 2. 好处
    • 3. 场景案例
    • 4. 案例源码
      • 1. 代码结构
      • 2. 公共榜单接口
      • 3. 公共榜单抽象父类
      • 4. 具体某个榜单实现类
      • 5. 单元测试代码

模板方法模式(行为模式)

1. 模板方法模式介绍

  • 父类定义算法骨架,细节的具体实现延迟到子类
  • 子类只是定义某些自己的个性化处理,但不改变执行顺序

2. 好处

  • 减少代码冗余,共性抽取后算法流程更加清晰与规范
  • 降低流程性错误的可能,开发类似的新需求,能通过父类算法骨架清晰理出流程
  • 定义了统一执行标准,便于管理与控制流程
  • 子类仅需要实现自己的个性化业务,修改自己的业务不会影响其他业务,符合单一职责原则

3. 场景案例

  • 项目中会开发大量实时变化的榜单,榜单的更新与查询流程基本都相同,如:公共校验、个性化校验、榜单排名获取、符合规则上榜、不符合规则下榜,但不同榜单校验方式与获取渠道不同
    • 使用模板方法模式
    • 接口添加为需要的抽象方法
    • 抽象类实现接口添加算法骨架与实现统一的抽象方法,同时使用接口的抽象方法
    • 具体的榜单需要继承抽象父类,实现抽象方法,然后调用算法骨架方法即可
  • 一对一视频聊有三中场景,执行流程均相同,但是计费规则等业务不同
    • 抽象父类定义执行顺序
    • 三种场景:一对一、直播转向、速配,直接继承父类处理规则,然后实现自己具体的计费规则

4. 案例源码

1. 代码结构

├── IRank.java
├── base
│   └── AbstractBaseRank.java
├── bean
│   ├── RankDelReq.java
│   └── RankMessageReq.java
├── ext
│   └── ExtServiceImpl.java
└── impl
    └── RankServiceImpl.java

2. 公共榜单接口

public interface IRank {

    /**
     * 公用判断,是否被审核处罚等处理,返回判断结果
     * @return {@code 0} 正常、不用下榜(也可使用 Enum 枚举)
     */
    int getCommLimit(long userId);

    /**
     * 个性化判断,返回判断结果
     * @return {@code 0} 正常、不用下榜(也可使用 Enum 枚举)
     */
    int getRankUp(long userId);

    /**
     * 查询用户在榜单的分数
     * @return 返回分数 Redis 中 zset 数据结构的 score
     *          {@code null} 分数为 null 需要下榜单,
     */
    Double getScore(long userId);

    /**
     * 用户上榜
     * @return {@code true} 用户上榜成功
     */
    boolean doRankUp(RankDelReq rankDelReq);

    /**
     * 用户下榜
     * @return {@code true} 用户下榜成功
     */
    boolean doRankDown(RankDelReq rankDelReq);
}

3. 公共榜单抽象父类

  • 定义算法骨架,实现公共的方法
public abstract class AbstractBaseRank implements IRank {

    /**
     * 榜单处理流程
     */
    public void dealRank(RankMessageReq rankMessage) {

        // 判空
        if (rankMessage == null || rankMessage.getUserId() == null) {
            System.out.println(String.format("传入信息异常:%s", rankMessage));
            return;
        }

        long userId = rankMessage.getUserId();

        // 公共判断
        int commLimit = getCommLimit(userId);
        // 不符合则下榜
        if (commLimit != 0) {
            boolean rankDownFlag = doRankDown(new RankDelReq(userId, null, commLimit));
            System.out.println(String.format("用户:%s 不符合公共判断下榜单:%s 下榜结果:%s", userId, commLimit, rankDownFlag));
            return;
        }

        // 个性化判断
        int rankUpLimit = getRankUp(userId);
        // 不符合则下榜
        if (rankUpLimit != 0) {
            boolean rankDownFlag = doRankDown(new RankDelReq(userId, null, rankUpLimit));
            System.out.println(String.format("用户:%s 不符合个性化判断下榜单:%s 下榜结果:%s", userId, rankUpLimit, rankDownFlag));
            return;
        }

        // 返回分数
        Double score = getScore(userId);
        // 分数为 null 则下榜
        if (score == null) {
            boolean rankDownFlag = doRankDown(new RankDelReq(userId, score, null));
            System.out.println(String.format("用户:%s 没有分数判断下榜单 下榜结果:%s", userId, rankDownFlag));
            return;
        }

        boolean rankUpFlag = doRankUp(new RankDelReq(userId, score, null));
        System.out.println(String.format("用户:%s 上榜结果:%s", userId, rankUpFlag));
    }

    /**
     * 公用判断,是否被审核处罚等处理,返回判断结果
     * @return {@code 0} 正常、不用下榜(也可使用 Enum 枚举)
     */
    @Override
    public int getCommLimit(long userId) {

        // 被冻结
        if (ExtServiceImpl.isFreeze(userId)) {
            System.out.println(String.format("用户:%s 被冻结:%s", userId));
            return 1;
        }

        // 被禁玩
        if (ExtServiceImpl.isBan(userId)) {
            System.out.println(String.format("被禁玩:%s", userId));
            return 2;
        }

        // 榜单审核中
        if (ExtServiceImpl.isRankAudit(userId)) {
            System.out.println(String.format("榜单审核中:%s", userId));
            return 3;
        }

        return 0;
    }
}

4. 具体某个榜单实现类

public class RankServiceImpl extends AbstractBaseRank {

    /**
     * 个性化判断,返回判断结果
     * @return {@code 0} 正常、不用下榜(也可使用 Enum 枚举)
     */
    @Override
    public int getRankUp(long userId) {
        System.out.println("个性化判断");
        return 0;
    }

    /**
     * 查询用户在榜单的分数
     * @return 返回分数 Redis 中 zset 数据结构的 score
     *          {@code null} 分数为 null 需要下榜单,
     */
    @Override
    public Double getScore(long userId) {
        System.out.println("查询用户在榜单的分数");
        return 0D;
    }

    /**
     * 用户上榜
     * @return {@code true} 用户上榜成功
     */
    @Override
    public boolean doRankUp(RankDelReq rankDelReq) {
        System.out.println("用户上榜");
        return true;
    }

    /**
     * 用户下榜
     * @return {@code true} 用户下榜成功
     */
    @Override
    public boolean doRankDown(RankDelReq rankDelReq) {
        System.out.println("用户下榜");
        return true;
    }
}

5. 单元测试代码

public class RankServiceImplTest {

    @Test
    public void test() {
        RankServiceImpl rankServiceImpl = new RankServiceImpl();

        System.out.println(String.format("用户:%s 榜单处理", 2808253L));
        rankServiceImpl.dealRank(new RankMessageReq(2808253L));

        System.out.println();
        System.out.println(String.format("用户:%s 榜单处理", null));
        rankServiceImpl.dealRank(new RankMessageReq(null));
    }
}

你可能感兴趣的:(设计模式,模板方法模式,java,设计模式)