🗺️背景
物流行业的新规后,消费者在购物平台购买商品后,收货信息保存在购物平台,一般不会交给发货方,当仓库需要发货时要通过购物平台对接不同的快递公司完成发货
本来的需求只有一个来源平台,后续有了不同的客户和不同的来源平台,我不想在原来的人的下面叠if else,所以使用了策略模式进行修改,后续再有不同的来源平台,只要增加实现类就可以了
👓正文
使用策略模式配合工厂模式 来解决上面的问题
✏️定义策略接口
定义一个策略接口 其中包含了所有策略都要实现的方法
/**
* @Title ILogisticsWaybill
* @Description 电子面单相关对接的策略接口
* @Version 1.0
*/
public interface ILogisticsWaybill {
/**
* 获取对应电子面单平台的名字
* @return 例如: 抖音 快手
*/
String getWaybillName();
/**
* 是否使用这个实现
* @param customerOrderSource 客户订单来源
* @return 是否
*/
boolean useFlag(String customerOrderSource);
/**
* 一些公用的方法
*/
void common();
/**
* 订单受理时根据订单信息 向平台下单创建电子面单
* @param o 订单信息
* @return 快递单信息
*/
LogisticsCreateOrderResDto createOrder(LogisticsCreateOrderDto o);
/**
* 取消 快递单号
* @param cancelDto 取消单号等信息
* @return 成功与否
*/
boolean cancelOrder(WaybillCancelDto cancelDto);
}
🖋️实现具体策略
1.定义一个抽象实现类
定义一个抽象的实现类,包含了启动初始化 和 一个公用的方法
/**
* @Title BaseLogisticsWaybill
* @Description 共用的算法逻辑抽象类
* @Version 1.0
*/
public abstract class BaseLogisticsWaybill implements InitializingBean,ILogisticsWaybill{
private Logger logger = LoggerFactory.getLogger(BaseLogisticsWaybill.class);
@Override
public void afterPropertiesSet() {
logger.info("初始化" + this.getWaybillName() + "策略的实现类");
LogisticWaybillFactory.registerStrategy(this.getWaybillName(),this);
}
@Override
public void common() {
// 公用方法 在不同策略执行完后 共同的操作 比如
logger.info("记录日志");
logger.info("推送通知");
}
}
2.定义具体实现类
- 菜鸟
/**
* @Title CaiNiaoWaybillImpl
* @Description 菜鸟电子面单相关接口实现类
* @Version 1.0
*/
@Slf4j
@Component
public class CaiNiaoWaybillImpl extends BaseLogisticsWaybill {
@Override
public String getWaybillName() {
return WaybillNameEnum.CAI_NIAO.getCode();
}
/**
* 直接返回false 不参与循环判断
* @param customerOrderSource 客户订单来源
* @return false
*/
@Override
public boolean useFlag(String customerOrderSource) {
return false;
}
@Override
public LogisticsCreateOrderResDto createOrder(LogisticsCreateOrderDto o) {
log.info("通过 菜鸟 下单");
return new LogisticsCreateOrderResDto("菜鸟");
}
/**
* 菜鸟取消快递单号
*/
@Override
public boolean cancelOrder(WaybillCancelDto cancelDto) {
return true;
}
}
- 抖音
/**
* @Title DouYinOrderUtils
* @Description 抖音订单电子面单
* @Version 1.0
*/
@Slf4j
@Component
public class DouYinWaybillImpl extends BaseLogisticsWaybill {
@Override
public String getWaybillName() {
return WaybillNameEnum.DOU_YIN.getCode();
}
@Override
public boolean useFlag(String customerOrderSource) {
return "DOUYIN".equals(customerOrderSource) || "DYXD".equals(customerOrderSource) || "douyin".equals(customerOrderSource);
}
@Override
public LogisticsCreateOrderResDto createOrder(LogisticsCreateOrderDto o) {
log.info("通过 抖音 下单");
return new LogisticsCreateOrderResDto("抖音");
}
@Override
public boolean cancelOrder(WaybillCancelDto cancelDto) {
return false;
}
}
- 拼多多
/**
* @Title PddWaybillImpl
* @Description 拼多多电子面单接口实现类
* @Version 1.0
*/
@Slf4j
@Component
public class PddWaybillImpl extends BaseLogisticsWaybill {
@Override
public String getWaybillName() {
return WaybillNameEnum.PDD.getCode();
}
/**
* 是否使用此实现类
* @param customerOrderSource 客户订单来源
* @return 是否
*/
@Override
public boolean useFlag(String customerOrderSource) {
return "pdd".equals(customerOrderSource);
}
@Override
public LogisticsCreateOrderResDto createOrder(LogisticsCreateOrderDto o) {
log.info("通过 拼多多 下单");
return new LogisticsCreateOrderResDto("拼多多");
}
@Override
public boolean cancelOrder(WaybillCancelDto cancelDto) {
return false;
}
}
实际对接有很多不同的平台,传入的参数也比较复杂,先做个简单示例.
3.枚举 和 一些domain
/**
* @Title WaybillNameEnum
* @Description 电子面单平台枚举
* @Version 1.0
*/
@Getter
public enum WaybillNameEnum {
/**
* 对接的电子面单平台
*/
CAI_NIAO("cainiao","菜鸟"),
DOU_YIN("douyin","抖音"),
PDD("pdd","拼多多"),
;
private final String code;
private final String name;
WaybillNameEnum(String code, String name) {
this.code = code;
this.name = name;
}
}
/**
* @Title LogisticsUpdateOrder
* @Description 创建电子面单接口的 dto
* @Version 1.0
*/
@Data
public class LogisticsCreateOrderDto {
/**
* .....省略
*/
}
/**
* @Title LogisticsUpdateOrder
* @Description 创建电子面单 接口的返回值
* @Version 1.0
*/
@Data
@AllArgsConstructor
public class LogisticsCreateOrderResDto {
private String remark;
/**
* 省略.......
*/
}
/**
* @Title WaybillCancelDto
* @Description 电子面单 取消对象
* @Version 1.0
*/
@Data
public class WaybillCancelDto {
/**
* 省略.......
*/
}
🏭︎定义一个工厂类
主要是定义一个map,在项目启动的时候把各个实现类保存到map中,然后在调用的时候根据customerOrderSource客户订单来源字段来获取对应的实现类
/**
* @Title LogisticWaybillFactory
* @Description 电子面单接口实现类工厂
* @Version 1.0
*/
@Slf4j
@Component
public class LogisticWaybillFactory {
/**
* 保存 策略实现类 <接口名,具体策略实现类>
*/
private final static Map<String, BaseLogisticsWaybill> STRATEGY_MAP = new ConcurrentHashMap<>(16);
/**
* 添加策略实例
*/
public static void registerStrategy(String type, BaseLogisticsWaybill strategy) {
STRATEGY_MAP.put(type, strategy);
}
/**
* 获取策略实例
* 根据实现类里的 useFlag 判断当前实现类是否使用 如果都不用 就用菜鸟的
* @param customerOrderSource 客户订单来源 有时候即使是同一个平台 也会有多个不同的字符串
* @return 实现类实例
*/
public static BaseLogisticsWaybill getImpl(String customerOrderSource) {
for (BaseLogisticsWaybill value : STRATEGY_MAP.values()) {
if (value.useFlag(customerOrderSource)){
return value;
}
}
return STRATEGY_MAP.get(WaybillNameEnum.CAI_NIAO.getCode());
}
/**
* 根据map的key来获取实现类 有的客户固定一个平台 用这个
* @param mapKey map的key
* @return 实现类实例
*/
public static BaseLogisticsWaybill getImplByKey(WaybillNameEnum mapKey) {
return STRATEGY_MAP.get(mapKey.getCode());
}
}
🧑🏭定义一个策略执行类
/**
* @Title LogisticWaybillExec
* @Description 电子面单接口执行类
* @Version 1.0
*/
@Service
public class LogisticWaybillExec {
/**
* 创建电子面单
* @param customerOrderSource 客户订单来源
* @param dto 订单信息
* @return 电子面单信息
*/
public LogisticsCreateOrderResDto createOrder(String customerOrderSource, LogisticsCreateOrderDto dto){
BaseLogisticsWaybill impl = LogisticWaybillFactory.getImpl(customerOrderSource);
LogisticsCreateOrderResDto order = impl.createOrder(dto);
// 通用方法
impl.common();
return order;
}
/**
* 取消电子面单
* @param customerOrderSource 客户订单来源
* @param cancelDto 取消的对象
* @return 取消成功与否
*/
public boolean cancelOrder(String customerOrderSource,WaybillCancelDto cancelDto) {
BaseLogisticsWaybill impl = LogisticWaybillFactory.getImpl(customerOrderSource);
return impl.cancelOrder(cancelDto);
}
}
🎣接口调用实现
/**
* @Title StrategyController
* @Description 接口
* @Version 1.0
*/
@RestController
@RequestMapping("/execute")
public class StrategyController {
@Autowired
LogisticWaybillExec logisticWaybillExec;
@GetMapping("/{strategyName}")
public String executeStrategy(@PathVariable String strategyName) {
LogisticsCreateOrderResDto order = logisticWaybillExec.createOrder(strategyName, new LogisticsCreateOrderDto());
return "来源: " + order.getRemark();
}
}
可以看到在项目启动时,会初始化各个实现类
1.传入参数 DYXD
可以看到根据传入的参数调用了对应的实现类,并且触发了公用的方法
2.传入参数 pdd
3.传入任意参数
这次的示例里菜鸟是兜底的,传入的参数找不到对应的实现类时,默认使用菜鸟的实现类,以实际业务情况为准
🔚总结
以上就是我的这次策略模式实践,结合策略模式和工厂模式,减少if else的叠加,简化后续维护😁