【设计模式】策略模式在项目中的实战运用-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

目录

前言思考

随着业务需求不断迭代更新系统逻辑越来越复杂。if else堆砌让人眼花缭乱。
那么此时就可以考虑使用设计模式重构代码逻辑

采用什么设计模式或者哪几种设计模式组合与实际业务场景、逻辑有关系

以下面这个场景为例

现在要将一批货物从A地点运往B地点涉及三方始发方、目的方、运输媒介方货物如果在此时发生了丢失那么具体是哪一方的责任
现在承担包裹问题责任方有四种始发方、目的方、始发方和目的方、运输媒介方
每种责任方处理结果都有不同的处理方案例如如果是运输丢失那么司机的人力公司需要承担这部分赔偿如果是始发地则这部分物品成本需要挂在始发地等等

如果用if else 去写责任方处理结果的方法那么就要嵌套好几层如下伪代码所示

if(jugmentParty == "始发方"){
	//始发方处理
}else if(jugmentParty == "目的方"){
	//目的方处理
}else if(jugmentParty == "始发方和目的方"){
	//始发方和目的方处理
}else{
	//运输媒介方处理
}

责任方会随着业务场景不断增加可能还会增加快递员的责任这么无限套下去这个方法中的代码会长并且不易扩展每次都要动这个方法的东西很难不保证其他正常运行的逻辑不受影响非常不符合单一职责原则和开闭原则。

并且业务侧希望可以按照明细商品纬度处理责任或者整个运单包裹纬度处理责任这有什么区别呢例如说一个包裹里面有9个杯子只有一个碎了那实际上只需要赔偿一个杯子的价格即可整单责任。例如包裹丢失等。

这种情况下就可以抽象一下业务场景无论有多少责任方要做的事情都是包裹责任处理处理分两种一种是单个商品纬度处理一种是整个包裹纬度处理。然后针对不同责任方有不同的处理细节。

这种场景我觉得十分适合使用策略模式。

实现落地

1首先创建一个统一接口类定义整包裹处理和单商品处理的方法

@Component("JudgmentStrategy")
public interface JudgmentStrategy {
    /**
     * 整包裹处理责任
     * @param confirmReq
     */
    void wholeOrderOperate(BizJudgmentConfirmReq confirmReq);

    /**
     * 单个商品处理责任
     * @param confirmReq
     */
    void singleOrderOperate(BizJudgmentConfirmReq confirmReq);
}

2创建不同责任方的实现类实现此接口的两个方法

始发地

/**
 * 始发地责任处理
 */
@Slf4j
@Component("Origin")
public class OriginJudgmentStrategy implements JudgmentStrategy{
    @Override
    public void wholeOrderOperate(BizJudgmentConfirmReq confirmReq) {
        //始发地整单处理责任结果
    }

    @Override
    public void singleOrderOperate(BizJudgmentConfirmReq confirmReq) {
        //始发地单个商品处理责任结果
    }
}

目的地责任处理

/**
 * 目的地责任处理
 */
@Slf4j
@Component("Dest")
public class DestJudgmentStrategy implements JudgmentStrategy{
    @Override
    public void wholeOrderOperate(BizJudgmentConfirmReq confirmReq) {
        //始发地整单处理责任结果
    }

    @Override
    public void singleOrderOperate(BizJudgmentConfirmReq confirmReq) {
        //始发地单个商品处理责任结果
    }
}

始发地&目的地双方责任处理

/**
 * 始发地&目的地双方责任处理
 */
@Slf4j
@Component("OriginAndDest")
public class OriginAndDestJudgmentStrategy implements JudgmentStrategy{
    @Override
    public void wholeOrderOperate(BizJudgmentConfirmReq confirmReq) {
        //始发地整单处理责任结果
    }

    @Override
    public void singleOrderOperate(BizJudgmentConfirmReq confirmReq) {
        //始发地单个商品处理责任结果
    }
}

运输方责任处理

/**
 * 运输方责任处理
 */
@Slf4j
@Component("Transport")
public class TransportJudgmentStrategy implements JudgmentStrategy{
    @Override
    public void wholeOrderOperate(BizJudgmentConfirmReq confirmReq) {
        //始发地整单处理责任结果
    }

    @Override
    public void singleOrderOperate(BizJudgmentConfirmReq confirmReq) {
        //始发地单个商品处理责任结果
    }
}

3创建一个Context类用于关联调用方和策略方法的一个媒介也叫上下文。也就是说我们真正去调用策略方法是不直接引用JudgmentStrategy的而且引用Context类。
上面通过工厂的形式创建策略类的实现类直接通过@Autowired注入到Context上下文中。

@Slf4j
@Component
public class JudgmentStrategyContext {

    private final Map<String, JudgmentStrategy> strategyMap = new ConcurrentHashMap<String, JudgmentStrategy>();

    /**
     * 注入所有实现了JudgmentStrategy接口的Bean
     *
     * @param strategyMap
     */
    @Autowired
    public void JudgmentStrategy(Map<String, JudgmentStrategy> strategyMap) {
        this.strategyMap.clear();
        strategyMap.forEach(this.strategyMap::put);
    }

    /**
     * 整单处理库存
     *
     * @param confirmReq
     */
    public void wholeOrderOperate(BizJudgmentConfirmReq confirmReq) {
        log.info("JudgmentStrategyContext#wholeOrderOperate--入参confirmReq:{}", JSONObject.toJSONString(confirmReq));      	
        strategyMap.get(getBean(confirmReq)).wholeOrderOperate(confirmReq);
    }

     /**
     * 明细纬度处理库存
     *
     * @param confirmReq
     */
    public void singleOrderOperate(BizJudgmentConfirmReq confirmReq) {
        log.info("JudgmentStrategyContext#singleOrderOperate--入参confirmReq:{}", JSONObject.toJSONString(confirmReq));
        strategyMap.get(getBean(confirmReq)).singleOrderOperate(confirmReq);
    }

    private String getBean(BizJudgmentConfirmReq confirmReq) {
	    if(confirmReq == null || confirmReq.getJudgmentParty() == null){
	    	throw new BusinessException("责任处理失败责任方入参不能空");
	    }
        HandoverJudgmentPartyEnum partyEnum = HandoverJudgmentPartyEnum.getEnumByValue(confirmReq.getJudgmentParty());
        if (partyEnum == null) {
            throw new BusinessException("责任处理失败未匹配到处理责任方");
        }
        switch (partyEnum) {
        	case TRANSPORT:
                return "Transport";
            case ORIGIN:
                return "Origin";
            case DEST:
                return "Dest";
            case ORIGIN_AND_DEST:
                return "OriginAndDest";
            default:
                throw new BusinessException("责任处理失败未匹配到处理责任方");
        }
    }
}

补充枚举类以及入参类

/**
 * 责任方枚举
 */
public enum HandoverJudgmentPartyEnum {
    TRANSPORT(1, "运输媒介方"),
    ORIGIN(2, "始发方"),
    DEST(3, "目的方"),
    ORIGIN_AND_DEST(4, "始发方&目的方"),
    ;

    private Integer value;
    private String name;

    HandoverJudgmentPartyEnum(Integer value, String name) {
        this.value = value;
        this.name = name;
    }

    public Integer getValue() {
        return value;
    }

    public String getName() {
        return name;
    }

    public static String getNameByValue(Integer value){
        for(HandoverJudgmentPartyEnum sourceEnum: HandoverJudgmentPartyEnum.values()){
            if(sourceEnum.getValue().equals(value)){
                return sourceEnum.getName();
            }
        }
        return null;
    }

    public static HandoverJudgmentPartyEnum getEnumByValue(Integer value) {
        if (null == value) {
            return null;
        }
        for (HandoverJudgmentPartyEnum item : HandoverJudgmentPartyEnum.values()) {
            if (Objects.equals(value,item.value)) {
                return item;
            }
        }
        return null;
    }
}
/**
 * 责任方确认入参
 */
@Data
public class BizJudgmentConfirmReq {
    /**
     * 单号编码
     */
    private String businessCode;

    /**
     * 责任方 {@linkplain com.jd.hr.domain.enums.HandoverJudgmentPartyEnum}
     */
    private Integer judgmentParty;

    /**
     * 责任依据
     */
    private String judgmentBasis;

    /**
     * 操作人erp
     */
    private String operateUser;

    /**
     * 操作人姓名
     */
    private String operateName;
}

4调用策略方法

@Slf4j
@Service
public class JugmentOrderServiceImpl extends IJugmentOrderService{
	@Resource
    private JudgmentStrategyContext judgmentStrategyContext;
    
	@Override
    public void wholeOrderOperateTest(BizJudgmentConfirmReq confirmReq) {
        judgmentStrategyContext.wholeOrderOperate(confirmReq);
    }
    @Override
    public void singleOrderOperateTest(BizJudgmentConfirmReq confirmReq) {
        judgmentStrategyContext.singleOrderOperate(confirmReq);
    }
}

小结

通过上面的代码实现可以看出接口类只负责业务策略的定义定义各个方面策略的标准策略的具体实现可以认为是多个方面的策略或者是多个角色的策略就有不同的实现。

Context上下文类负责业务逻辑的编排封装了策略的执行细节具体的实现服务只需要调用Context类的方法而不需要了解具体策略对象的实现细节。如何编排策略完全在Context封装好。

策略模式的优势
通过策略模式或变种的应用实现了面向接口而非实现编程满足了职责单一、开闭原则从而达到了功能上的高内聚低耦合、提高了可维护性、扩展性以及代码的可读性。

设计模式是为了帮助我们从复杂的业务场景中解脱出来提升代码的可读性可维护性。但是在实际应用过程中也不必拘泥于设计模式本身也可以结合所使用的框架进行变种处理。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6