通过策略+工厂模式来重构多个版本的下单流程

举报
幼儿园老大* 发表于 2024/11/27 15:49:18 2024/11/27
【摘要】 看到这里相信大家对用户生成订单的流程有了详细的了解了,针对锁的分类、锁的粒度、同步转异步的优化等分别写出了4个生成订单的版本,而且用于分布式锁使用了切面的方式,在Controller和Servic层中添加了Lock层,来实现切面的锁当写到这里发现Lock层设计的有点冗余,最好是去掉,还有就是生成订单的版本是多个,如果后续再加新版本的话,类与类之间的单一职责原则和开闭原则就不是那么符合了所以经...

看到这里相信大家对用户生成订单的流程有了详细的了解了,针对锁的分类、锁的粒度、同步转异步的优化等分别写出了4个生成订单的版本,而且用于分布式锁使用了切面的方式,在Controller和Servic层中添加了Lock层,来实现切面的锁


当写到这里发现Lock层设计的有点冗余,最好是去掉,还有就是生成订单的版本是多个,如果后续再加新版本的话,类与类之间的单一职责原则和开闭原则就不是那么符合了


所以经过上述原因,将生成订单的多个版本流程进行重构,将Lock层去掉,使用策略+工厂模式来优化下单流程。

本次优化是在Controller和Lock层进行,Service层并不受到影响,不影响大家学习之前讲解完的业务。

策略模式

这4个版本的生成订单流程其实就是对应着不同的生成订单策略,所以这种场景使用策略模式再适合不过了,那么既然是策略,我们就要定义一个生成订单策略的接口,来规定生成订单的行为


生成订单策略接口

public interface ProgramOrderStrategy {
    
    /**
     * 创建订单
     * @param programOrderCreateDto 订单参数
     * @return 订单编号
     * */
    String createOrder(ProgramOrderCreateDto programOrderCreateDto);
}


当定义好生成订单策略的接口后,就开始实现不同版本生成订单的具体策略了,就是将之前Lock层的生成订单逻辑移动到各自版本的生成订单策略中


已生成订单版本1为例:

@Component
public class ProgramOrderV1Strategy implements ProgramOrderStrategy {
    
    @Autowired
    private ProgramOrderService programOrderService;
    
    @Autowired
    private CompositeContainer compositeContainer;
    
    
    @RepeatExecuteLimit(
            name = RepeatExecuteLimitConstants.CREATE_PROGRAM_ORDER,
            keys = {"#programOrderCreateDto.userId","#programOrderCreateDto.programId"})
    @ServiceLock(name = PROGRAM_ORDER_CREATE_V1,keys = {"#programOrderCreateDto.programId"})
    @Override
    public String createOrder(final ProgramOrderCreateDto programOrderCreateDto) {
        compositeContainer.execute(CompositeCheckType.PROGRAM_ORDER_CREATE_CHECK.getValue(),programOrderCreateDto);
        return programOrderService.create(programOrderCreateDto);
    }
}


当把这四个版本的生成订单策略逻辑都移动好了后,就开始思考如何去调用这些实现好的策略,如果此时我们要具体调用的话,就会写成下面这样:

@Operation(summary  = "购票V1")
@PostMapping(value = "/create/v1")
public ApiResponse<String> createV1(@Valid @RequestBody ProgramOrderCreateDto programOrderCreateDto) {
    ProgramOrderStrategy programOrderStrategy = SpringUtil.getBean(ProgramOrderV1Strategy.class);
    String orderNumber = programOrderStrategy.createOrder(programOrderCreateDto);
    return ApiResponse.ok(orderNumber);
}


在Controller层的方法中,先获取 ProgramOrderV1Strategy 的bean对象,通过向上转型 ProgramOrderStrategy 类型来接收,然后调用定好的策略方法 createOrder 来生成订单,获取订单编号,最后返回即可。


整个流程是没有问题的,但是总感觉差点意思,不是那么的优化,也就是从Spring获取对象的过程

SpringUtil.getBean(ProgramOrderV1Strategy.class);


工厂模式

我们不应该从Spring容器中直接拿取ProgramOrderV1Strategy 具体的实现策略,这样对扩展性并不友好,如果后续改成在ProgramOrderCreateDto入参中加入订单版本参数,前端直接传入版本就能直接调用对应的生成订单版本,目前这种方式就不符合了


所以需要更好的方式来取得具体的策略实现,而什么结构和这种业务符合呢?通过一个参数直接能命中拿到一个具体策略?也就是说通过key能拿到Value?


我都说了通过Key想拿到Value,那自然是Map结构了啊!那么再思考需要线程安全吗?还是思考流程,我们要什么时候往Map中放入策略?那肯定是策略这个对象构建好了,就可以放进入了,而这些策略对象通过通过Spring来管理,而Spring加载对象是线程安全的,所以我们直接使用HashMap就可以了,不需要考虑线程安全问题了


既然我们确定好选用HashMap作为存放策略对象的容器后,那就要再设计一个上下文,来操作这个HashMap


存放生成订单策略实现的上下文

public class ProgramOrderContext {
    
    private static final Map<String,ProgramOrderStrategy> MAP = new HashMap<>(8);
    
    public static void add(String version,ProgramOrderStrategy programOrderStrategy){
        MAP.put(version,programOrderStrategy);
    }
    
    public static ProgramOrderStrategy get(String version){
        return Optional.ofNullable(MAP.get(version)).orElseThrow(() -> 
                new DaMaiFrameException(BaseCode.PROGRAM_ORDER_STRATEGY_NOT_EXIST));
    }
}


这个上下文就是所谓的工厂,当我们传入版本参数后,此工厂就能直接给我们想要的对应版本策略。

上下文有了后,下面就来具体思考什么时候往上下文里放了,刚才我说过了,当策略对象创建好后,就可以往里放了,那么Spring管理的对象就提供了初始化行为的操作,比如:CommandLineRunner、InitializingBean、@PostConstruct、ApplicationListener<ApplicationStartedEvent>


如果小伙伴一路过来对项目组件学习的比较熟的话,应该眼熟,我在管理初始化行为组件中介绍过这四个初始化行为,所以,依旧要使用此组件来将这些策略对象放到上下文中,这里使用的初始化行为类型是CommandLineRunner,也就是要实现初始化行为组件中AbstractApplicationCommandLineRunnerHandler


版本枚举

在从上下文获取和放入的方法中,版本参数是使用字符串,通过ProgramOrderVersion枚举中的version来指定

public enum ProgramOrderVersion {
    /**
     * 版本
     * */
    V1_VERSION("v1","v1版本"),
    
    V2_VERSION("v2","v2版本"),
   
    V3_VERSION("v3","v3版本"),
    
    V4_VERSION("v4","v4版本"),
    ;

    private final String version;

    private final String msg;

    ProgramOrderVersion(String version, String msg) {
        this.version = version;
        this.msg = msg;
    }

    public String getVersion() {
        return version;
    }
    

    public String getMsg() {
        return this.msg == null ? "" : this.msg;
    }
    

    public static String getMsg(String version) {
        for (ProgramOrderVersion re : ProgramOrderVersion.values()) {
            if (re.version.equals(version)) {
                return re.msg;
            }
        }
        return "";
    }

    public static ProgramOrderVersion getRc(String version) {
        for (ProgramOrderVersion re : ProgramOrderVersion.values()) {
            if (re.version.equals(version)) {
                return re;
            }
        }
        return null;
    }
}


讲解到这里,我们的具体实现策略才真正的成型,下面就将每个具体的实现策略粘贴出来


生成订单v1策略

@Component
public class ProgramOrderV1Strategy extends AbstractApplicationCommandLineRunnerHandler implements ProgramOrderStrategy {
    
    @Autowired
    private ProgramOrderService programOrderService;
    
    @Autowired
    private CompositeContainer compositeContainer;
    
    
    @RepeatExecuteLimit(
            name = RepeatExecuteLimitConstants.CREATE_PROGRAM_ORDER,
            keys = {"#programOrderCreateDto.userId","#programOrderCreateDto.programId"})
    @ServiceLock(name = PROGRAM_ORDER_CREATE_V1,keys = {"#programOrderCreateDto.programId"})
    @Override
    public String createOrder(final ProgramOrderCreateDto programOrderCreateDto) {
        compositeContainer.execute(CompositeCheckType.PROGRAM_ORDER_CREATE_CHECK.getValue(),programOrderCreateDto);
        return programOrderService.create(programOrderCreateDto);
    }
    
    @Override
    public Integer executeOrder() {
        return 1;
    }
    
    @Override
    public void executeInit(final ConfigurableApplicationContext context) {
        ProgramOrderContext.add(ProgramOrderVersion.V1_VERSION.getVersion(),this);
    }
}

生成订单v2策略

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。