通过策略+工厂模式来重构多个版本的下单流程
看到这里相信大家对用户生成订单的流程有了详细的了解了,针对锁的分类、锁的粒度、同步转异步的优化等分别写出了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策略
- 点赞
- 收藏
- 关注作者
评论(0)