【设计模式-我的思考】套餐模式
imom排故下线-订单状态集查询功能实现(套餐模式)
需求描述
排故下线状态
不排故:未启用排故下线确认配置&订单状态参数值!=(170、180、190)
待排故:启用排故下线确认配置&排故下线确认未执行&订单状态参数值!=(170、180、190)
已排故:启用排故下线确认配置&排故下线确认已执行&订单状态参数值!=(170、180、190)
已入库:订单状态参数值=170、180、190
每种状态的条件是与关系,状态集之间是或关系,多选则查询集合的并集.每个订单只属于其中一种状态集.
需要实现状态集查询,和结果集元素状态标记返回.
套餐模式
这是一种很常见的需求场景,就是对查询条件进行提前设置好套餐,根据用户选择的套餐,实现原本需要复杂配置的多个查询条件组合.
原来的查找条件分字段拆解给用户,用户需要各种条件组合才能看到自己想要的,BA通过对查询条件的组合抽象出来4个状态集合,每个集合包含一系列的字段组合查询,这样的交互模式为状态模式,将复杂留给开发,将便捷留给用户.
分析一下,原来的查询条件是自由组装的,现在在状态集的范围内,是个固定的参数值组合.很容易联想到状态模式来根据用户的选择实现条件的组合放入对象中初始化,选择多个状态则初始化多个对象放入对象集合进行循环查询,将结果集再进行组装.
于是做出如下设计:
关键代码
TroubleCheckStatus.java //套餐查询对象
public void init(List<String> statusList){
List<WipOrderInfoQO> queryQO=new List();
//接收用户套餐选择,对子类对象进行初始化
if(statusList ==null){
WipOrderInfoQO object=new WipOrderInfoQO(this);
queryQO.add(object);
}else{
for(String status:statusList){
WipOrderInfoQO object=new WipOrderInfoQO(status,this);
queryQO.add(object);
}
}
this.queryList=queryQO;
}
WipOrderInfoQO.java//原有查询对象
public WipOrderInfoQO(TroubleCheckStatus object){
//将父类的属性初始化到子类对象,实现传递
}
public WipOrderInfoQO(String status,TroubleCheckStatus object){
//status为套餐名称,可以从枚举或者spring bean中获取配置好的数据,对WipOrderInfoQO的属性进行定制化,套餐分解
}
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.Getter;
/**
* className com.sany.mom.qm.troubleshooting.enums.TroubleCheckSetEnum 排故下线状态
* 不排故:未启用排故下线确认配置&订单状态参数值!=(170、180、190) 待排故:启用排故下线确认配置&排故下线确认未执行&订单状态参数值!=(170、180、190)
* 已排故:启用排故下线确认配置&排故下线确认已执行&订单状态参数值!=(170、180、190) 已入库:订单状态参数值=170、180、190
*
* @author shengfq
* @date 2024-10-15 11:32
*/
@Getter
public enum TroubleCheckSetEnum {
NONE("NONE", "不排故", false, false, onProgress()),
TODO("TODO", "待排故", true, false, onProgress()),
DONE("DONE", "已排故", true, true, onProgress()),
DONE1("DONE1", "已排故", false, true, onProgress()),
STOCKED("STOCKED", "已入库", null, null, stockedProgress());
private static final Map<String, TroubleCheckSetEnum> STATUS_MAP = Arrays.stream(TroubleCheckSetEnum.values())
.collect(Collectors.toMap(TroubleCheckSetEnum::getCode, Function.identity()));
/**
* 代码
*/
private String code;
/**
* 中文描述
*/
private String desc;
/**
* 是否启用排故下线确认配置
*/
private Boolean enable;
/**
* 排故下线确认状态
*/
private Boolean executed;
/**
* 订单状态子集合
*/
private List<Integer> progressStatus;
TroubleCheckSetEnum(String code, String desc, Boolean enable, Boolean executed, List<Integer> progressStatus) {
this.code = code;
this.desc = desc;
this.enable = enable;
this.executed = executed;
this.progressStatus = progressStatus;
}
/**
* 非入库状态
*/
private static List<Integer> onProgress() {
List<Integer> all= Arrays.stream(ProgressStatusEnum.values())
.map(ProgressStatusEnum::getCode).collect(Collectors.toList());
all.removeAll(stockedProgress());
return all;
}
/**
* 已入库:订单状态参数值=170、180、190
*/
private static List<Integer> stockedProgress() {
return Arrays.asList(ProgressStatusEnum.DELETED.getCode(), ProgressStatusEnum.CLOSED.getCode(),
ProgressStatusEnum.STORAGE.getCode());
}
/**
* 根据code获取排故下线实例
*/
public static TroubleCheckSetEnum ofCode(String code) {
return STATUS_MAP.get(code);
}
/**
* 根据订单的相关配置信息来计算其排故下线状态实例
*
* @param enable 订单类型是否启用排故下线
* @param executed 订单是否执行了排故下线确认
* @param progressStatus 订单状态
*/
public static TroubleCheckSetEnum getInstance(Boolean enable, Boolean executed, Integer progressStatus) {
TroubleCheckSetEnum troubleCheckSetEnum= Arrays.stream(TroubleCheckSetEnum.values()).filter(i -> i.progressStatus.contains(progressStatus)).findAny().get();
if(troubleCheckSetEnum==STOCKED){
return troubleCheckSetEnum;
}else{
return Arrays.stream(TroubleCheckSetEnum.values()).filter(i -> i.progressStatus.contains(progressStatus) && i.enable.equals(enable) && i.executed.equals(executed)).findAny().get();
}
}
}
总结
在企业级应用开发中,查询条件是复杂的,如果能通过设置套餐来降低系统使用程度,这个产品设计原则就是很好的idea.技术上实现,则是在原有的查询上下文上增加一个状态集父类,通过初始化将套餐与枚举绑定,读取枚举配置好的配方参数对原有参数进行组合,代码侵入程度低,可扩展性强,耦合度低,实则是一个很不错的设计方案.
2024-10-22 晚