The background,

Currently has a application service, because this involves change in product demand, after the complete technical solution, and then look at the need to change the code involved, discovered that a service and implementation class, the implementation class provides different scenarios of the three elements of validation logic, overall validation logic is to check for business rules, if the test is not passed, The wrapper returns service verification information; If the verification succeeds, the current identity authentication information is stored in the database.

However, based on the current production requirements, it is simply a matter of adding a member method to the Service, implementing the class to implement the method, copying the business logic of the other member methods, modifying it slightly, and then implementing the production requirements business logic. However, when you look at the current service class and see that many of its member methods have similar business logic, you can’t bear to make do with it, and then you can think of a better way to refactor the existing code.

Second, the analysis

Let’s take a look at the original code we want to refactor

1, CustomerRelationService

Interface code

public interface CustomerRelationService {
    OpenIdRelation getBindedRelationByOpenId(String openId);
		/** * XXX Binding information *@param dto
     * @return* /
    RespDTO bindCustomer(BindOpenIdRequestDTO bindOpenIdRequestDTO);

    /** * XXX Binding information *@param dto
     * @return* /
    RespDTO releaseBindCustomer(BindOpenIdRequestDTO dto);
    /** * XXX Binding information *@param dto
     * @return* /
    RespDTO refundBindOpenId(BindOpenIdRequestDTO dto);

    List<OpenIdRelation> queryRecordsByIdno(String idno);
}
Copy the code

2, CustomerRelationServiceImpl

Corresponding implementation class:

@Service
@Slf4j
public class CustomerRelationServiceImpl implements CustomerRelationService {

    private static final String OPEN_ID = "openId";

    private static final String STATUS = "status";

    private static final int STATUS_BINDED = 0;

    static final int UPPER_LIMIT_ERROR_COUNT = 10;

    @Autowired
    private OpenIdRelationMapper openIdRelationMapper;

    @Autowired
    private CustomerMapper customerMapper;

    @Autowired
    private MortgageService mortgageService;

    @Autowired
    MsgCacheManager msgCacheManager;

    @Autowired
    OrderCenterHttpApi orderCenterHttpApi;

    @Autowired
    MortgageHttpApi mortgageHttpApi;

    @Override
    public OpenIdRelation getBindedRelationByOpenId(String openId) {
        Map<String, Object> param = Maps.newHashMap();
        param.put(OPEN_ID, openId);
        param.put(STATUS, STATUS_BINDED);
        OpenIdRelation openIdRelation = openIdRelationMapper.selectByParam(param);
        return Optional.ofNullable(openIdRelation).orElse(null);
    }

    /** * XX bind *@param verifyCode
     * @param mobile
     * @param idNo
     * @param realName
     * @param openId
     * @return* /
    @Override
    public RespDTO bindCustomer(String verifyCode, String mobile, String idNo, String realName, String openId) {
        long totalCount = msgCacheManager.getRecordErrorCount(mobile);
        if(totalCount >= UPPER_LIMIT_ERROR_COUNT){
            return RespDTO.fail(String.format("SMS verification code error exceeds %s times, try again 1 minute later!",totalCount));
        }
        String verifyCodeFromCache = msgCacheManager.getBindCacheValue(mobile);
        log.info("[Binding client relationship retrieved from cache to verifyCode = {}]", verifyCodeFromCache);
        if (verifyCodeFromCache == null| |! Objects.equals(verifyCodeFromCache, verifyCode)) { log.error("[Failed to match binding client verification code openId={}]", openId);
            msgCacheManager.recordErrorCount(mobile);
            return RespDTO.fail("SMS verification code error, please fill in again.");
        }
        msgCacheManager.clearRecordErrorCount(mobile);
        List<MortgageDetailDTO> list = mortgageHttpApi.getMortgageDetailByIdNo(idNo);
        if (CollectionUtils.isEmpty(list)) {
            return RespDTO.fail("Your identity information was not found, please confirm and re-submit.");
        }
        MortgageDetailDTO mortgageDetailDTO = list.stream()
                .filter(mortgageDetail-> Objects.equals(mortgageDetail.getApiCustomerVO().getCustomerMobile(), mobile)
                        && Objects.equals(mortgageDetail.getApiCustomerVO().getCustomerName(), realName)).findFirst().orElse(null);
        if (ObjectUtils.isEmpty(mortgageDetailDTO)) {
            log.error("[Binding client submission information does not match system information openId = {}]", openId);
            return RespDTO.fail("Your identity information was not found, please confirm and re-submit.");
        }
        OpenIdRelation isExist = getBindedRelationByOpenId(openId);
        if(isExist ! =null) {
            log.error("[Bind client relationship user information bind openId = {}]", openId);
            return RespDTO.success();
        }

        Customer customer = Customer.builder().idno(idNo).realName(realName).mobile(mobile).build();
        long insertCount = customerMapper.insertSelective(customer);
        if(insertCount ! =0) {
            OpenIdRelation openIdRelation = OpenIdRelation.builder().customerId(customer.getId()).openId(openId).idNo(idNo).status(OpenIdRelation.BIND_STATUS)
                    .bindTime(new Date()).build();
            openIdRelationMapper.insertSelective(openIdRelation);
        }

        return RespDTO.success();
    }


    /** * Verify the verification code. If an error occurs, * is returned@return* /
    private RespDTO verifyCode(BindOpenIdRequestDTO dto){
        String cacheValue = msgCacheManager.getBindCacheValue(dto.getMobile());
        return VerifyCodeUtil.verify(cacheValue, dto.getOpenId(), dto.getVerifyCode());
    }

    /** * XX Binding information *@param dto
     * @return* /
    @Override
    public RespDTO releaseBindCustomer(BindOpenIdRequestDTO dto) {
        //1. Verify the verification code
        RespDTO resp = verifyCode(dto);
        if (RespStatusEnum.FAIL.getStatus() == resp.getStatus()){
            return resp;
        }

        //2. Query customer information from the order center. If no customer information exists, return
        String idNo = dto.getIdNo();
        String openId = dto.getOpenId();
        List<OrderDetailVO> list = orderCenterHttpApi.queryOrderInfoByIdNo(idNo);
        OrderDetailVO detailVO = list.stream().filter(o -> Objects.nonNull(o.getApplicantDetailVO())
                && Objects.equals(dto.getRealName(), o.getApplicantDetailVO().getName())
                && (Objects.equals(dto.getMobile(), o.getApplicantDetailVO().getMobile())
                    || Objects.equals(dto.getMobile(), o.getApplicantDetailVO().getMobile2()))).findAny().orElse(null);
        if (Objects.isNull(detailVO)){
            log.error("[openId = {}]", openId);
            return RespDTO.fail("Your identity information was not found, please confirm and re-submit.");
        }

        //3, check whether the binding, binding returns
        if (null! = getBindedRelationByOpenId(openId)) { log.error("[openId = {}]", openId);
            return RespDTO.success();
        }

        //4. If no binding is performed, the binding is performed
        bind(dto);
        return RespDTO.success();
    }

    /** * XXX bind *@param dto
     * @return* /
    @Override
    public RespDTO refundBindOpenId(BindOpenIdRequestDTO dto) {
        //1. Verify the verification code
        RespDTO respDTO = verifyCode(dto);
        if (RespStatusEnum.FAIL.getStatus() == respDTO.getStatus()){
            return respDTO;
        }

        //2. Query customer information from the order center. If no customer information exists, return
        String openId = dto.getOpenId();
        RespDTO<TransferRefundOrderRe> resp = mortgageHttpApi.queryRefundOrder(dto.getIdNo());
        TransferRefundOrderRe refundVO = resp.getData();
        if (Objects.isNull(refundVO)){
            log.error("[Refund binding customer relationship submission information does not match system information openId = {}]", openId);
            return RespDTO.fail("Your identity information was not found, please confirm and re-submit.");
        }

        //3, check whether the binding, binding returns
        if (null! = getBindedRelationByOpenId(openId)) { log.error("[Refund binding Customer relationship user information is bound openId = {}]", openId);
            return RespDTO.success();
        }

        //4. If no binding is performed, the binding is performed
        bind(dto);
        return RespDTO.success();
    }

    @Override
    public List<OpenIdRelation> queryRecordsByIdno(String idno) {
        List<OpenIdRelation> list = openIdRelationMapper.queryRecordsByIdno(idno);
        return list;
    }

    /** * Save the binding data *@param dto
     */
    private void bind(BindOpenIdRequestDTO dto){
        Customer customer = Customer.builder().idno(dto.getIdNo()).realName(dto.getRealName()).mobile(dto.getMobile()).build();
        long insertCount = customerMapper.insertSelective(customer);
        if(insertCount ! =0) {
            openIdRelationMapper.insertSelective(
                    OpenIdRelation.builder()
                            .customerId(customer.getId()).openId(dto.getOpenId()).idNo(dto.getIdNo())
                            .status(OpenIdRelation.BIND_STATUS).bindTime(newDate()).build() ); }}}Copy the code

The above classes have three important methods as follows. The above implementation classes are business logic implementations based on different scenarios of the three methods

RespDTO bindCustomer(BindOpenIdRequestDTO bindOpenIdRequestDTO);

/ * * *@param dto
 * @return* /
RespDTO releaseBindCustomer(BindOpenIdRequestDTO dto);
/ * * *@param dto
 * @return* /
RespDTO refundBindOpenId(BindOpenIdRequestDTO dto);
Copy the code

In general, the idea of refactoring is to define an abstract class through a template method, then three different methods are three different implementation classes of the abstract class, and the same code logic (i.e., pre-business validation + binding) is abstracted in the abstract class, exposing a public method.

Then through an entrusted agency, through online text objects to encapsulate different types of business scenarios, and entrust the corresponding implementation class to handle the business logic of abstract classes, so that if add the binding method, only need to increase the corresponding subclass, colleagues to modify a scene, and you just need to modify the corresponding subclass, that conform to the principle of open and close.

Three, refactoring

1. UML design

Overall design idea

  • By defining abstract classesAbstractBindHandler, encapsulate the steps of the three-factor scene verification, and the subclass only needs to implement the pre-verification abstract method.
  • By definingBindContextContext object that encapsulates the request parameters and output that the entire process depends on.
  • By defining the enumeration class Biz, and then implementing the increment responseHandler, just add members to the enumeration class.
  • By definingBindHandlerDispatcherTo realizeHandlerEncapsulation, for external business calls only need to followBindHandlerDispatcherInteractive, shieldHandlerClass details.
  • If you want to add service scenarios, you only need to add themHandlerClass. If you want to modify the corresponding service scenario, you only need to find the corresponding service scenarioHandlerClass can be modified, that is, in line with the open and closed principle.

2, BindContext

This class contains three common properties, param output parameters, Handler class depends on the parameters passed through Param, this type is a generic, need to define the Handler class declaration, Biz is an enumeration class.

/ * * *@description: customer binding context object *@Date : 2021/10/29 5:52 PM
 * @Author: Shi Dongdong -Seig Heil */
@Data
@Builder
public class BindContext<P> {
    /** * DTO parameter */
    private P param;
    /** * Service type */
    private Biz biz;
    /** * Response message */
    private RespDTO respDTO;

    /** * Service type */
    public enum Biz{
        /** * XXX payment client binding */
        ESC_PAYMENT_BIND,
        /**
         * XXX退款客户绑定
         */
        ESC_REFUND_BIND,
        /** ** XXX unbind the client */
        ESC_RELEASE_BIND,
        /** ** XXX unbind the client */
        CRZ_RELEASE_BIND
    }
}
Copy the code

3, AbstractBindHandler

The base class of the Handler class, again a generic abstract class, needs to subclass to declare that the input parameter Param needs to inherit from BindOpenIdRequestDTO.

/ * * *@descriptionAbstract public account customer three element binding processor *@Date : 2021/10/29 5:51 PM
 * @Author: Shi Dongdong -Seig Heil */
public abstract class AbstractBindHandler<P extends BindOpenIdRequestDTO> {
    /** * The binding identifier */
    final String BIND_TAG = "BIND";

    @Autowired
    DiamondConfigProxy diamondConfigProxy;

    @Autowired
    OpenIdRelationMapper openIdRelationMapper;

    @Autowired
    CustomerMapper customerMapper;

    @Resource
    CustomerRelationService customerRelationService;

    @Autowired
    MortgageService mortgageService;

    @Autowired
    MsgCacheManager msgCacheManager;

    @Autowired
    OrderCenterHttpApi orderCenterHttpApi;

    @Autowired
    MortgageHttpApi mortgageHttpApi;
    /** ** pre-check *@paramContext *@return* /
    abstract RespDTO verify(BindContext<P> context);

    /** * External exposure method *@param context
     */
    public void handle(BindContext<P> context){
        RespDTO respDTO = verify(context);
        context.setRespDTO(respDTO);
        boolean canBind = respDTO.getStatus() ==  RespStatusEnum.SUCCESS.getStatus() && BIND_TAG.equals(respDTO.getData());
        if(canBind){ bind(context.getParam()); context.setRespDTO(RespDTO.success()); }}/** * Save the binding data *@param dto
     */
    private void bind(BindOpenIdRequestDTO dto){
        Customer customer = Customer.builder().idno(dto.getIdNo()).realName(dto.getRealName()).mobile(dto.getMobile()).build();
        long insertCount = customerMapper.insertSelective(customer);
        if(insertCount ! =0) {
            openIdRelationMapper.insertSelective(
                    OpenIdRelation.builder()
                            .customerId(customer.getId()).openId(dto.getOpenId()).idNo(dto.getIdNo())
                            .status(OpenIdRelation.BIND_STATUS).bindTime(newDate()).build() ); }}/** * Query the bound relationship according to openId *@param openId
     * @return* /
    protected OpenIdRelation getBindedRelationByOpenId(String openId) {
        return customerRelationService.getBindedRelationByOpenId(openId);
    }

    /** * Verify the verification code. If an error occurs, * is returned@return* /
    RespDTO verifyCode(BindOpenIdRequestDTO dto){
        String cacheValue = msgCacheManager.getBindCacheValue(dto.getMobile());
        returnVerifyCodeUtil.verify(cacheValue, dto.getOpenId(), dto.getVerifyCode()); }}Copy the code

Related implementation classes

CrzReleaseBindHandler

@Service
@Slf4j
public class CrzReleaseBindHandler extends AbstractBindHandler<BindOpenIdRequestDTO>{

    @Autowired
    MortgageHttpApi mortgageHttpApi;

    @Override
    RespDTO verify(BindContext<BindOpenIdRequestDTO> context) {
        BindOpenIdRequestDTO dto = context.getParam();
        //1. Verify the verification code
        RespDTO respDTO = verifyCode(dto);
        if (RespStatusEnum.FAIL.getStatus() == respDTO.getStatus()){
            return respDTO;
        }
        MockConfig mockConfig = diamondConfigProxy.mockConfig();
        List<String> idnoMockList = mockConfig.getReleaseIdNoMockList();
        // The id whitelist ignores the three-factor verification
        boolean whiteIdno = null! = idnoMockList && idnoMockList.contains(dto.getIdNo());if(! whiteIdno){//2
            CrzReleaseBindVerifyDTO bindVerifyDTO = CrzReleaseBindVerifyDTO.builder()
                    .customerIdno(dto.getIdNo()).customerMobile(dto.getMobile()).customerName(dto.getRealName())
                    .build();
            try {
                RespDTO<String> bindResult = mortgageHttpApi.bindVerify(bindVerifyDTO);
            } catch (InvokeException e) {
                log.error("[CrzReleaseBindHandler#verify]",e);
                returnRespDTO.fail(e.getErrMsg()); }}//2, check whether the binding, binding returns
        String openId = dto.getOpenId();
        if (null! = getBindedRelationByOpenId(openId)) { log.error("[Refund binding Customer relationship user information is bound openId = {}]", openId);
            return RespDTO.success();
        }
        returnRespDTO.success(BIND_TAG); }}Copy the code

EscPaymentBindHandler

/ * * *@description: Used car payment customer binding processor *@Date : 2021/10/29 6:01 PM
 * @Author: Shi Dongdong -Seig Heil */
@Service
@Slf4j
public class EscPaymentBindHandler extends AbstractBindHandler<BindOpenIdRequestDTO>{
    /** * The upper limit for the number of incorrect SMS verification codes */
    final int UPPER_LIMIT_ERROR_COUNT = 10;

    @Override
    RespDTO verify(BindContext<BindOpenIdRequestDTO> context) {
        BindOpenIdRequestDTO dto = context.getParam();
        String verifyCode = dto.getVerifyCode(), mobile = dto.getMobile(), idNo = dto.getIdNo(), realName = dto.getRealName(), openId = dto.getOpenId();
        long totalCount = msgCacheManager.getRecordErrorCount(mobile);
        if(totalCount >= UPPER_LIMIT_ERROR_COUNT){
            return RespDTO.fail(String.format("SMS verification code error exceeds %s times, try again 1 minute later!",totalCount));
        }
        String verifyCodeFromCache = msgCacheManager.getBindCacheValue(mobile);
        log.info("[Binding client relationship retrieved from cache to verifyCode = {}]", verifyCodeFromCache);
        if (verifyCodeFromCache == null| |! Objects.equals(verifyCodeFromCache, verifyCode)) { log.error("[Failed to match binding client verification code openId={}]", openId);
            msgCacheManager.recordErrorCount(mobile);
            return RespDTO.fail("SMS verification code error, please fill in again.");
        }
        msgCacheManager.clearRecordErrorCount(mobile);
        List<MortgageDetailDTO> list = mortgageHttpApi.getMortgageDetailByIdNo(idNo);
        if (CollectionUtils.isEmpty(list)) {
            return RespDTO.fail("Your identity information was not found, please confirm and re-submit.");
        }
        MortgageDetailDTO mortgageDetailDTO = list.stream()
                .filter(mortgageDetail-> Objects.equals(mortgageDetail.getApiCustomerVO().getCustomerMobile(), mobile)
                        && Objects.equals(mortgageDetail.getApiCustomerVO().getCustomerName(), realName)).findFirst().orElse(null);
        if (ObjectUtils.isEmpty(mortgageDetailDTO)) {
            log.error("[Binding client submission information does not match system information openId = {}]", openId);
            return RespDTO.fail("Your identity information was not found, please confirm and re-submit.");
        }
        OpenIdRelation isExist = getBindedRelationByOpenId(openId);
        if(isExist ! =null) {
            log.error("[Bind client relationship user information bind openId = {}]", openId);
            return RespDTO.success();
        }

        Customer customer = Customer.builder().idno(idNo).realName(realName).mobile(mobile).build();
        long insertCount = customerMapper.insertSelective(customer);
        if(insertCount ! =0) {
            OpenIdRelation openIdRelation = OpenIdRelation.builder().customerId(customer.getId()).openId(openId).idNo(idNo).status(OpenIdRelation.BIND_STATUS)
                    .bindTime(new Date()).build();
            openIdRelationMapper.insertSelective(openIdRelation);
        }

        returnRespDTO.success(BIND_TAG); }}Copy the code

EscRefundBindHandler

/ * * *@description: Used car refund customer binding processor *@Date : 2021/10/29 6:01 PM
 * @Author: Shi Dongdong -Seig Heil */
@Service
@Slf4j
public class EscRefundBindHandler extends AbstractBindHandler<BindOpenIdRequestDTO>{

    @Override
    RespDTO verify(BindContext<BindOpenIdRequestDTO> context) {
        BindOpenIdRequestDTO dto = context.getParam();
        //1. Verify the verification code
        RespDTO respDTO = verifyCode(dto);
        if (RespStatusEnum.FAIL.getStatus() == respDTO.getStatus()){
            return respDTO;
        }

        //2. Query customer information from the order center. If no customer information exists, return
        String openId = dto.getOpenId();
        RespDTO<TransferRefundOrderRe> resp = mortgageHttpApi.queryRefundOrder(dto.getIdNo());
        TransferRefundOrderRe refundVO = resp.getData();
        if (Objects.isNull(refundVO)){
            log.error("[Refund binding customer relationship submission information does not match system information openId = {}]", openId);
            return RespDTO.fail("Your identity information was not found, please confirm and re-submit.");
        }

        //3, check whether the binding, binding returns
        if (null! = getBindedRelationByOpenId(openId)) { log.error("[Refund binding Customer relationship user information is bound openId = {}]", openId);
            return RespDTO.success();
        }
        returnRespDTO.success(BIND_TAG); }}Copy the code

EscReleaseBindHandler

/ * * *@description: Used car dealership customer binding processor *@Date : 2021/10/29 6:01 PM
 * @Author: Shi Dongdong -Seig Heil */
@Service
@Slf4j
public class EscReleaseBindHandler extends AbstractBindHandler<BindOpenIdRequestDTO>{

    @Override
    RespDTO verify(BindContext<BindOpenIdRequestDTO> context) {
        BindOpenIdRequestDTO dto = context.getParam();
        //1. Verify the verification code
        RespDTO resp = verifyCode(dto);
        if (RespStatusEnum.FAIL.getStatus() == resp.getStatus()){
            return resp;
        }

        //2. Query customer information from the order center. If no customer information exists, return
        String idNo = dto.getIdNo();
        String openId = dto.getOpenId();
        List<OrderDetailVO> list = orderCenterHttpApi.queryOrderInfoByIdNo(idNo);
        OrderDetailVO detailVO = list.stream().filter(o -> Objects.nonNull(o.getApplicantDetailVO())
                && Objects.equals(dto.getRealName(), o.getApplicantDetailVO().getName())
                && (Objects.equals(dto.getMobile(), o.getApplicantDetailVO().getMobile())
                || Objects.equals(dto.getMobile(), o.getApplicantDetailVO().getMobile2()))).findAny().orElse(null);
        if (Objects.isNull(detailVO)){
            log.error("[Unbinding client relationship submission information does not match system information openId = {}], openId);
            return RespDTO.fail("Your identity information was not found, please confirm and re-submit.");
        }

        //3, check whether the binding, binding returns
        if (null! = getBindedRelationByOpenId(openId)) { log.error(OpenId = {}]", openId);
            return RespDTO.success();
        }
        returnRespDTO.success(BIND_TAG); }}Copy the code

4, BindHandlerDispatcher

This class acts as a Handler coordinator, directing the corresponding Handler class based on the type biz of the context

/ * * *@description: customer relationship binding handles dispensers *@Date : 2021/10/29 6:19 PM
 * @Author: Shi Dongdong -Seig Heil */
@Component
public class BindHandlerDispatcher {

    @Resource
    EscPaymentBindHandler escPaymentBindHandler;

    @Resource
    EscReleaseBindHandler escReleaseBindHandler;

    @Resource
    EscRefundBindHandler escRefundBindHandler;

    @Resource
    CrzReleaseBindHandler crzReleaseBindHandler;

    /** * Distribute the corresponding processor according to the business type to process *@param context
     */
    public void dispatch(BindContext context){
        switch (context.getBiz()){
            case ESC_PAYMENT_BIND:
                escPaymentBindHandler.handle(context);
                return;
            case ESC_REFUND_BIND:
                escRefundBindHandler.handle(context);
                return;
            case ESC_RELEASE_BIND:
                escReleaseBindHandler.handle(context);
                return;
            case CRZ_RELEASE_BIND:
                crzReleaseBindHandler.handle(context);
                return; }}}Copy the code

5, CustomerRelationServiceImpl

After refactoring, the member methods here simply construct the BindContext instance object and call the dispatcher.dispatch member methods, and the code looks much cleaner.

/** * XX bind *@param dto
 * @return* /
@Override
public RespDTO bindCustomer(BindOpenIdRequestDTO dto) {
    BindContext<BindOpenIdRequestDTO> context = BindContext.<BindOpenIdRequestDTO>builder().biz(BindContext.Biz.ESC_PAYMENT_BIND).param(dto).build();
    bindHandlerDispatcher.dispatch(context);
    return context.getRespDTO();
}

/** * XX Binding information *@param dto
 * @return* /
@Override
public RespDTO releaseBindCustomer(BindOpenIdRequestDTO dto) {
    BindContext<BindOpenIdRequestDTO> context = BindContext.<BindOpenIdRequestDTO>builder().biz(BindContext.Biz.ESC_RELEASE_BIND).param(dto).build();
    bindHandlerDispatcher.dispatch(context);
    return context.getRespDTO();
}

/** * XX bind *@param dto
 * @return* /
@Override
public RespDTO releaseBindCustomerForCrz(BindOpenIdRequestDTO dto) {
    BindContext<BindOpenIdRequestDTO> context = BindContext.<BindOpenIdRequestDTO>builder().biz(BindContext.Biz.CRZ_RELEASE_BIND).param(dto).build();
    bindHandlerDispatcher.dispatch(context);
    return context.getRespDTO();
}

/** * XX bind *@param dto
 * @return* /
@Override
public RespDTO refundBindOpenId(BindOpenIdRequestDTO dto) {
    BindContext<BindOpenIdRequestDTO> context = BindContext.<BindOpenIdRequestDTO>builder().biz(BindContext.Biz.ESC_REFUND_BIND).param(dto).build();
    bindHandlerDispatcher.dispatch(context);
    return context.getRespDTO();
}
Copy the code

Four,

Through comparison before and after the reconstruction, we found that the whole Service class (CustomerRelationServiceImpl) at this time of the code through the Handler class to encapsulate the, and the Service class simply delegate the whole class the Dispatcher, This is done by constructing the BindContext instance object and then calling its Dispatch method.

No changes are made to the methods provided by the service as a whole. If you add member methods later, you only need to add Handler classes.