How clever use of the Strategy pattern in Spring Boot project to get rid of if else!

Straight into the subject

We all know that design patterns (Design Pattern) predecessors is a summary of the code development experience, is to solve the problem of a series of specific routines. It is not defined syntax, but a set of solutions can be used to improve code reusability, maintainability, readability, robustness and security.
Well, we might have to find out about design patterns, but how used in the project may still be a bit confused, today, the company projects a scene just to let me use a design patterns: Strategy pattern.

Scenes

About prepaid customer orders (orders to pay the same reason), we all know, today's payment is very much, for example: Alipay, micro-channel, CUP, wallet (APP individual account balance) and so on.
Queries entity Query:

/**
 * @author Howinfun
 * @desc 查询
 * @date 2019/10/22
 */
@Data
public class ChargeQuery {
    
    /** 支付方式(ALI/WX/UNION) */
    @NotBlank(message = "支付方式不能为空",groups = PayWayNotBlank.class)
    private String payWay;
    
    /** 充值金额 */
    @NotNull(message = "充值金额不能为空",groups = AmountNotNull.class)
    private Double amount;
}

Service Interface:

/**
 * @author Howinfun
 * @desc 充电-充值模块
 * @date 2019/10/30
 */
public interface ChargeRechargeService {

    /**
     * 根据不用支付方式进行用户余额充值
     * @param query
     * @return
     */
    Result recharge(ChargeQuery query);

    /**
     * 充值回调
     * @param rechargeCallBack
     */
    Result rechargeCallBack(RechargeCallBack rechargeCallBack);
}

Traditional manner

If else is using conditional or switch to:

/**
 * @author Howinfun
 * @desc
 * @date 2019/10/30
 */
@Service
@AllArgsConstructor
@Slf4j
public class ChargeRechargeServiceImpl implements ChargeRechargeService {

    private final CarUserMapper carUserMapper;
    private final IncomePaymentMapper incomePaymentMapper;
    private final RechargeRecordMapper rechargeRecordMapper;
    private final PayWayHandlerContext payWayHandlerContext;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result recharge(ChargeQuery query) {
        Result result = new Result();
        // ......
        // ......
        if (PayConstant.PAY_WAY_WX.equals(query.getPayWay())){
            // 微信
            // ......
        }else if (PayConstant.PAY_WAY_ALI.equals(query.getPayWay())){
            // 支付宝
            // ......

        }else if (PayConstant.PAY_WAY_UNION_PAY.equals(query.getPayWay())){
             // 银联
             // ......
    
        }
        return result;
    }
}

Summary: We can see that the traditional implementation is very cumbersome, and the code is not very simple, poor scalability. If we want to access the new payment method, then we can only continue to add else if.

Strategy Mode

Talk is cheap, show me the code .
We look at, if you use the strategy pattern, service code will become sawed.

/**
 * @author Howinfun
 * @desc
 * @date 2019/10/30
 */
@Service
@AllArgsConstructor
@Slf4j
public class ChargeRechargeServiceImpl implements ChargeRechargeService {

    private final PayWayHandlerContext payWayHandlerContext;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result recharge(ChargeQuery query) {
        return this.payWayHandlerContext.getHandlerInstance(query.getPayWay()).handler(query);
    }
}

emmmm, really a lot simpler. Not only less code, and concise, and no longer worry because the new payment method to modify serviceImpl code.

The following detailed explanation:
1, first of all, we need to customize a note to identify a type corresponding to a payment processor.

/**
 * @author Howinfun
 * @desc 自定义注解,标识支付类型
 * @date 2019/11/2
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface PayWayHandler {

    String value();
}

2. Next, a abstract processor, the processor of each payment inherit this abstract processor, implement handle method:

/**
 * @author Howinfun
 * @desc 抽象订单类型处理器
 * @date 2019/11/2
 */
public abstract class AbstractPayWayHandler {

    /**
     *
     * @param query
     * @return
     */
    abstract public Result handler(ChargeQuery query);
}

3, implementation class, such as Alipay, WeChat CUP:
Note: Each processor should add @component, to the Spring management.

/**
 * @author Howinfun
 * @desc 支付宝支付
 * @date 2019/11/2
 */
@Component
@PayWayHandler("ALI")
@Slf4j
@AllArgsConstructor
public class AliPayWayHandler extends AbstractPayWayHandler {
    
    // ....各种依赖

    @Override
    public Result handler(ChargeQuery query) {
        Result result = new Result();
        // ......
        return result;
    }
}

/**
 * @author Howinfun
 * @desc 微信支付
 * @date 2019/11/2
 */
@Component
@PayWayHandler("WX")
@Slf4j
@AllArgsConstructor
public class WxPayWayHandler extends AbstractPayWayHandler {
    
    // ....各种依赖

    @Override
    public Result handler(ChargeQuery query) {
        Result result = new Result();
        // ......
        return result;
    }
}

/**
 * @author Howinfun
 * @desc 银联支付
 * @date 2019/11/2
 */
@Component
@PayWayHandler("UNION")
@Slf4j
@AllArgsConstructor
public class UnionPayWayHandler extends AbstractPayWayHandler {
    
    // ....各种依赖

    @Override
    public Result handler(ChargeQuery query) {
        Result result = new Result();
        // ......
        return result;
    }
}

4, and then came the most focused, create a class that implements the interface ApplicationContextAware, rewrite setApplicationContext method, and then scanning with a custom annotation @PayWayHandler of Bean, then stored, easy access Service.

/**
 * @author Howinfun
 * @desc
 * @date 2019/11/2
 */
@Component
public class PayWayHandlerContext implements ApplicationContextAware {

    @Autowired ApplicationContext applicationContext;

    /** key为PayWay,value为class*/
    private static final Map<String,Class> handlerMap = new HashMap<>(10);

    public AbstractPayWayHandler getHandlerInstance(String payType){
        Class clazz = handlerMap.get(payType);
        if (clazz == null){
            throw new CustomDeniedException("暂不支持此支付方式");
        }
        return (AbstractPayWayHandler) applicationContext.getBean(clazz);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 遍历带有PayTypeHandler注释的类
        Map<String,Object> beans = applicationContext.getBeansWithAnnotation(PayWayHandler.class);
        if (beans != null && beans.size() > 0) {
            for (Object serviceBean : beans.values()) {
                String payType = serviceBean.getClass().getAnnotation(PayWayHandler.class).value();
                handlerMap.put(payType, serviceBean.getClass());
            }
        }
    }
}

Summary: this, ServiceImpl handler may be selected according to the front end of the corresponding transmission over payWay processed. I use the strategy pattern simplifies complex if else code, and scalability has been greatly improved, no longer worry about because the new payment methods and modify business code.

Guess you like

Origin www.cnblogs.com/Howinfun/p/11785460.html