策略模式和模板模式

141 阅读2分钟

这两天学习了策略模式和模板模式,总结一下这两种模式。

一、策略模式

策略模式的使用场景主要是同一类行为的不同实现上,如

  • 不同类型的交易⽅式(信⽤卡、⽀付宝、微信)、
  • ⽣成唯⼀ID策略 (UUID、DB⾃增、DB+Redis、雪花算法、Leaf算法)

策略模式主要有下面这3个类

  • 方法接口
  • 具体的多个不同实现
  • 策略选择类

下面举一个具体的例子,在抽奖的时候,有不同的抽奖策略,下面这个例子有两个抽奖策略的实现

  1. 方法接口
public interface IDrawAlgorithm {
    /**
     * SecureRandom 生成随机数,索引到对应的奖品信息返回结果
     *
     * @param strategyId        策略ID
     * @param excludeAwardIds   排除掉已经不能作为抽奖的奖品ID,留给风控和空库存使用
     * @return                  中奖结果
     */
    String randomDraw(Long strategyId, List<String> excludeAwardIds);

}
  1. 策略实现类
  • 策略1
@Component("singleRateRandomDrawAlgorithm")
public class SingleRateRandomDrawAlgorithm extends BaseAlgorithm {

    @Override
    public String randomDraw(Long strategyId, List<String> excludeAwardIds) {
    	// 策略1实现
        return awardId;
    }

}
  • 策略2
@Component("entiretyRateRandomDrawAlgorithm")
public class EntiretyRateRandomDrawAlgorithm extends BaseAlgorithm {

    @Override
    public String randomDraw(Long strategyId, List<String> excludeAwardIds) {
    	// 策略2实现
        // 返回中奖结果
        return awardId;
    }

}
  1. 策略选择类
public class StrategyContext {
    private IDrawAlgorithm drawAlgorithm;

    public StrategyContext(IDrawAlgorithm drawAlgorithm) {
        this.drawAlgorithm = drawAlgorithm;
    }

    public String randomDraw(Long strategyId, List<String> excludeAwardIds) {
        return drawAlgorithm.randomDraw(strategyId, excludeAwardIds);
    }
}

测试:

    public static void main(String[] args) {
        StrategyContext strategyContext = new StrategyContext(new SingleRateRandomDrawAlgorithm());
        strategyContext.randomDraw(10001L, new ArrayList<>());
    }

二、模板模式

当一个事情可以流程化来做的时候,可以使用模板模式来开发,模版模式中有下面几个流程

  1. 定义接口
  2. 在抽象类中实现接口,并且将模板定义在抽象类中
  3. 具体的实现类继承抽象类,实现自己特有的逻辑,公共的逻辑在抽象类中实现

下面是一个抽奖的例子,将抽奖的流程固定在模板抽象类中

  1. 定义接口
public interface IDrawExec {

    /**
     * 抽奖方法
     * @param req 抽奖参数;用户ID、策略ID
     * @return    中奖结果
     */
    DrawResult doDrawExec(DrawReq req);

}
  1. 在抽象类中实现接口,并且将模板定义在抽象类中

这里面获取不在抽奖范围内的列表和执行抽奖算法步骤是包含业务逻辑的,这些可以在子类中实现,公共的放在抽象类中。

public abstract class AbstractDrawBase extends DrawStrategySupport implements IDrawExec {

    private Logger logger = LoggerFactory.getLogger(AbstractDrawBase.class);

    @Override
    public DrawResult doDrawExec(DrawReq req) {
        // 1. 获取抽奖策略
        StrategyRich strategyRich = super.queryStrategyRich(req.getStrategyId());
        Strategy strategy = strategyRich.getStrategy();

        // 2. 校验抽奖策略是否已经初始化到内存
        this.checkAndInitRateData(req.getStrategyId(), strategy.getStrategyMode(), strategyRich.getStrategyDetailList());

        // 3. 获取不在抽奖范围内的列表,包括:奖品库存为空、风控策略、临时调整等
        List<String> excludeAwardIds = this.queryExcludeAwardIds(req.getStrategyId());

        // 4. 执行抽奖算法
        String awardId = this.drawAlgorithm(req.getStrategyId(), drawAlgorithmGroup.get(strategy.getStrategyMode()), excludeAwardIds);

        // 5. 包装中奖结果
        return buildDrawResult(req.getuId(), req.getStrategyId(), awardId);
    }
  1. 具体的实现类继承抽象类,实现自己特有的逻辑
public class DrawExecImpl extends AbstractDrawBase {

    private Logger logger = LoggerFactory.getLogger(DrawExecImpl.class);

    @Override
    protected List<String> queryExcludeAwardIds(Long strategyId) {
        // 获取不在抽奖范围内的列表逻辑
        return awardList;
    }

    @Override
    protected String drawAlgorithm(Long strategyId, IDrawAlgorithm drawAlgorithm, List<String> excludeAwardIds) {
       // 抽奖逻辑
       return isSuccess ? awardId : null;
    }

}