包阅导读总结
1. 关键词:
– Java
– 场景执行工具
– 状态模式
– 工厂模式
– 代码扩展性
2. 总结:
本文以 Java 为例,探讨面对复杂业务场景时的代码优化,从原始条件分支逻辑到使用状态模式和工厂模式,最终实现通用的场景执行工具,减轻开发者负担,提升代码的扩展性和可读性,文中还介绍了淘天集团相关技术团队。
3. 主要内容:
– 软件开发中常需应对不同场景执行不同逻辑
– 以领红包为例,说明避免流水账代码会用模式和工具解决
– 介绍需求如用户开通服务根据不同状态做不同行为
– 阐述状态模式的场景处理接口、抽象类和具体实现类
– 指出仍需判断状态执行,用工厂模式优化
– 提出通用场景执行器,通过场景枚举管理避免重名
– 介绍场景抽象接口的判断方法和工厂类选择实现类的方式
– 说明使用场景执行器的步骤
– 介绍淘天集团丰富性行业技术团队
思维导图:
文章地址:https://mp.weixin.qq.com/s/LZ50iwoyocedc_DvjgEPPg
文章来源:mp.weixin.qq.com
作者:少杰
发布时间:2024/7/31 12:19
语言:中文
总字数:3109字
预计阅读时间:13分钟
评分:89分
标签:Java,设计模式,场景执行工具,大淘宝技术,状态模式
以下为原文内容
本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com
在日常开发过程中,经常遇到“根据不同场景,做不同的处理”。如:对领红包而言,不满足领红包资格的用户返回“不满足资格”文案。有资格而未领取的用户,需要领红包、纪录、发对账消息等操作。已经领过红包的用户,执行xxx的操作。
为了避免流水账的代码,会使用各种模式,建立抽象类、具体实现类、工厂类等去解决此问题。使得增加新场景,也具有更好的代码的扩展性,如:领取红包的用户,新增是否过期、是否使用的场景。
更进一步,开发了场景执行工具,简化开发流程。
优点:
类图
状态模型 + 工厂模式
代码详解
现在有一个需求,用户要开通xx服务,返回开通结果消息。开通结果消息有很多状态:开通成功、解约成功、冻结等等。需要根据这个消息的状态去,做不同的行为。
很容易想到状态模式,先定义一个通用的状态处理接口,再根据开通状态去创建各种具体的状态实现类,实现doCallback方法。如此,我们写出来的代码具有很好的扩展性和可读性。
▐状态模式
-
场景处理接口:
public interface SceneHandleBase<A,T,G> {
G doCallback(T params) ;
}
2. 场景抽象类:在场景处理接口基础之上,又封装了一层,主要用于打异常日志和监控
@Slf4j
public abstract class AbstractSceneHandleBase<A, T, G> implements SceneHandleBase<A, T, G> {
public abstract G execute(T params);
@Override
public G doCallback(T params) {
try {
return execute(params);
} catch (Exception e) {
log.error("{}, |StatusHandleBase_doCallback|error|,className:{}, doCallback, params:{}, msg:{}", EagleEye.getTraceId(), this.getClass().getSimpleName(), JSON.toJSONString(params), e.getMessage(), e);
throw e;
}
}
}
3. 具体场景实现类:
开通成功,执行写入签约表
@Component
@Slf4j
public class ContractStartedSceneHandleImpl extends AbstractSceneHandleBase<User, ContractEvent, TResult<Boolean>> {
@Resource
private PurchasePayLaterBizService purchasePayLaterBizService;
@Override
public boolean judge(User params) {
return true;
}
@Override
public TResult<Boolean> execute(ContractEvent contractEvent) {
UserSignResultuserSignResult=purchasePayLaterBizService.buildUserSignResult(contractEvent,SignStatus.SIGN_SUCCESS);
return purchasePayLaterBizService.updateSignStatus(userSignResult);
}
}
解约成功,执行写入签约表
@Component
@Slf4j
public class ContractClosedSceneHandleImpl extends AbstractSceneHandleBase<User, ContractEvent, TResult<Boolean>> {
@Resource
private PurchasePayLaterBizService purchasePayLaterBizService;
@Override
public boolean judge(User params) {
return true;
}
@Override
public TResult<Boolean> execute(ContractEvent contractEvent) {
UserSignResultuserSignResult=purchasePayLaterBizService.buildUserSignResult(contractEvent,SignStatus.SIGN_FAIL);
return purchasePayLaterBizService.updateSignStatus(userSignResult);
}
}
等等具体实现类……
这样写出来的代码,虽然简化了不少。我们仍然需要判断消息的状态,去执行不同具体实现类,在代码中还要写if/else。状态执行如:
if(ContractStatusEnum.valueOf(contractEvent.getStatus())==ContractStatusEnum.STARTED){
ContractStartedSceneHandleImpl.execute("x");
}else if(ContractStatusEnum.valueOf(contractEvent.getStatus())==ContractStatusEnum.CLOSE){
contractClosedSceneHandleImpl.execute("x");
}
......
更进一步优化,很容易想到用工厂模式来管理这些实现类。
▐ 工厂模式
场景注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface SceneHandleType {
String value();
}
在具体实现类上添加注解@SceneHandleType(“CLOSED”)
如此就可以通过工厂类,获取到实现类
public class SceneHandleFactory<A> implements ApplicationContextAware, InitializingBean {
private final Map<String, List<SceneHandleBase>> statusMap = new HashMap<>();
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
Map<String, SceneHandleBase> recommendStrategyMap = applicationContext.getBeansOfType(SceneHandleBase.class);
if (MapUtils.isEmpty(recommendStrategyMap)) {
return;
}
for (SceneHandleBase strategy : recommendStrategyMap.values()) {
SceneHandleType statusType = strategy.getClass().getAnnotation(SceneHandleType.class);
if (null == statusType) {
continue;
}
String statusValue = statusType.value();
if (null == statusValue) {
continue;
}
statusMap.put(statusValue, strategy);
}
}
public SceneHandleBase getSceneHandle(String status) {
return statusMap.get(statusType);
}
}
状态执行就变成:
statusHandle = statusFactory.getSceneHandle(contractEvent.getStatus();
TResult<Boolean> res = statusHandle.doCallback(contractEvent);
到了这里,整个执行方法就变的非常简洁,以后新增了一个开通状态,也只需要加一个实现类,代码的扩展性也得到了保证。
但类似的情况非常多,如开头讲到的领红包情况,每遇到一次都要写一堆代码,去实现工厂类,注解,抽象接口等等,将会浪费很多时间。
故更进一步,如果能抽出来一个通用的场景执行器,用户只需要考虑实现类,其余的接口、工厂类都给实现了,那岂不是大大提高效率。
▐ 场景执行器
public interface SceneEnumBase {
String getSceneName();
}
开通状态枚举类:
“具体场景实现类”上的注解@SceneHandleType值就是对应的枚举值全地址。
枚举值是什么?
public enum ContractStatusEnum implements SceneEnumBase {
STARTED,
FROZEN,
CLOSED,
NO_ENTRY;
@Override
public String getSceneName() {
return this.name();
}
}
具体场景实现类
注解是枚举值全地址
@SceneHandleType("*.ContractStatusEnum.CLOSED")
@Component
@Slf4j
public class ContractClosedSceneHandleImpl extends AbstractSceneHandleBase<User, ContractEvent, TResult<Boolean>> {
......
状态处理工厂类
获取具体实现类的方法
public class SceneHandleFactory<A> implements ApplicationContextAware, InitializingBean {
......
public SceneHandleBase getSceneHandle(SceneEnumBase status) {
String statusType = String.join(".", status.getClass().getName(), status.getSceneName());
return statusMap.get(statusType);
}
}
对于同一个场景枚举值,可能不止一个行为。如:用户已领取红包,这个红包已使用,执行a行为,红包未使用执行b行为。
此时,就用上了场景抽象接口的judgeConditions方法,此方法会在工厂类获取bean的方法中,选择合适的场景实现类。
public interface SceneHandleBase<A,T,G> {
Boolean judgeConditions(A params);
G doCallback(T params) ;
}
工厂类
public class SceneHandleFactory<A> implements ApplicationContextAware, InitializingBean {
private final Map<String, List<SceneHandleBase>> statusMap = new HashMap<>();
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
Map<String, SceneHandleBase> recommendStrategyMap = applicationContext.getBeansOfType(SceneHandleBase.class);
if (MapUtils.isEmpty(recommendStrategyMap)) {
return;
}
for (SceneHandleBase strategy : recommendStrategyMap.values()) {
SceneHandleType statusType = strategy.getClass().getAnnotation(SceneHandleType.class);
if (null == statusType) {
continue;
}
String statusValue = statusType.value();
if (null == statusValue) {
continue;
}
List<SceneHandleBase> list = statusMap.getOrDefault(statusValue, new ArrayList<>());
list.add(strategy);
statusMap.put(statusValue, list);
}
}
public SceneHandleBase getSceneHandle(SceneEnumBase status, A params) {
String statusType = String.join(".", status.getClass().getName(), status.getSceneName());
List<SceneHandleBase> bases = statusMap.get(statusType);
if (CollectionUtils.isNotEmpty(bases)) {
for (SceneHandleBase base : bases) {
if (base.judgeConditions(params)) {
return base;
}
}
}
return null;
}
}
使用
添加依赖
<dependency>
<groupId>*</groupId>
<artifactId>*</artifactId>
<version>*</version>
</dependency>
注入工厂bean
@Bean
public SceneHandleFactory getStatusHandleFactory() {
return new SceneHandleFactory();
}
根据条件定义场景枚举,并implements SceneEnumBase,getSceneName方法直接copy
public enum ContractStatusEnum implements SceneEnumBase {
STARTED,
FROZEN,
CLOSED,
NO_ENTRY;
@Override
public String getSceneName() {
return this.name();
}
}
场景实现类:
1. 添加注解
@Component
@StatusHandleType(“*.CJCreditContractStatusEnum.CLOSED”)
引号中是枚举全地址+枚举值
2. 实现两个方法
judge():同一个枚举值可以设置多个实现类,工厂类获取具体实现类时,根据此方法获取此枚举值的实现类
execute():具体实现类的实现方法
@StatusHandleType("*.ContractStatusEnum.CLOSED")
@Component
@Slf4j
public class ContractClosedSceneHandleImpl extends AbstractSceneHandleBase<User, ContractEvent, TResult<Boolean>> {
@Resource
private PurchasePayLaterBizService purchasePayLaterBizService;
@Override
public boolean judge(User params) {
return true;
}
@Override
public TResult<Boolean> execute(ContractEvent contractEvent) {
UserSignResult userSignResult = purchasePayLaterBizService.buildUserSignResult(contractEvent, SignStatus.SIGN_FAIL);
return purchasePayLaterBizService.updateSignStatus(userSignResult);
}
}
执行
SceneHandleBase<User, ContractEvent, TResult<Boolean>> statusHandle = statusFactory.getSceneHandle(ContractStatusEnum.valueOf(contractEvent.getStatus()),null);
TResult<Boolean> res = statusHandle.doCallback(contractEvent);
本文通过逐步深入的实践案例,阐述了从原始的条件分支逻辑到运用设计模式优化,最终实现高度抽象化的场景执行工具的全过程。这一过程不仅展示了技术深度,更重要的是体现了面向对象设计原则的应用价值,即通过高内聚低耦合的设计提升软件系统的灵活性与可扩展性。场景执行工具的提出,极大地减轻了开发者在面对多变业务场景时的编码负担,允许他们更加专注于业务逻辑的实现,而非繁琐的架构搭建,使得整个解决方案既强大又易于集成。
团队介绍
我们是淘天集团-丰富性行业技术团队。团队业务覆盖全淘宝天猫。我们深入不同垂直行业内部,以技术引领和促进行业变革。围绕商品、商家等要素,在商品的生产、流通、表达全链路上拥有领先于业界的技术积累。我们用技术帮助全平台商家和开发者,带来全平台业务的高效运营,并始终为消费者带来优质体验。