项目中的if else 太多了,要怎么重构?工厂模式实战

项目背景

最近在做一个物流配送的项目,对接了很多的快递公司,其中有一部分的业务逻辑是这样的:

        if (carrierName.equals("eleme")) {
            //向 eleme 下单
        } else if (carrierName.equals("sfsQiao")) {
            //向 sfsQiao 下单
        } else if (carrierName.equals("sfSamecity")) {
            //向 sfSamecity 下单
        }

就是根据物流公司的名称通知对应的物流公司配送,中间的逻辑代码都很长,如果都放在这种if else代码快中,代码很难维护也很丑,所以我们一开始用了工厂模式来处理这种情况。

具体实现

通过反射获取所有父类的实现类 Class ,然后根据传入的 carrierName 进行匹配,得到目标实现类。具体代码实现如下:

工厂类:找到所有同一目录下所有实现了 ICarrierService 的接口的Class,再取到所有名称带有Impl的实现类Class,根据Spring的SpringUtils得到bean的实例。

@Service
@Slf4j
//@PerformanceLog("Carrier service factory")
public class CarrierServiceFactory implements ICarrierServiceFactory {

    @Override
    public ICarrierService generateCarrierService(String carrierName) throws Exception {
        log.debug("[{}] - Hello I am [{}], I am creating carrier!", this.getClass(), carrierName);

        ICarrierService carrierService = SpringUtils.getBean(CarrierServiceImpl.class);
        if (StringUtils.isEmpty(carrierName)) {
            return carrierService;
        }

        Reflections reflections = new Reflections(this.getClass().getPackageName());
        Set> subTypes = reflections.getSubTypesOf(ICarrierService.class);

        log.info("Show subTypes Set [{}]",subTypes.toString());

        List> subTypeList
                = subTypes.stream().filter(x -> x.getName().contains("impl")).collect(Collectors.toList());

        ApplicationContext applicationContext = SpringUtils.getApplicationContext();
        log.debug("Env [{}] -> {}", applicationContext.getApplicationName(), applicationContext.getEnvironment().getActiveProfiles());

        for (Class subType : subTypeList){
            if (subType.getName().toLowerCase().contains(carrierName.split(" ")[0])){
                carrierService = SpringUtils.getBean(subType);
                log.debug("[{}] - Fetch carrierService : [{}]", carrierName, carrierService.getClass().getName());
            }
        }
        return carrierService;
    }
}

项目的包结构如下:

项目中的if else 太多了,要怎么重构?工厂模式实战_第1张图片

父类接口:

public interface ICarrierService {

    ApiResponseDto createOrder(BasicCarrierRequestDto basicCarrierRequestDto) throws Exception;
}

子类接口:

public interface ISamestoreService extends ICarrierService {
}

子类实现:

@Service
@Slf4j
public class SamestoreServiceImpl implements ISamestoreService {

    @Override
    public ApiResponseDto createOrder(BasicCarrierRequestDto basicCarrierRequestDto){
        log.info(basicCarrierRequestDto.getCarrierName()+" create order =================");
        return ApiResponseDto.success();
    }
}

调用:

@PostMapping(value = "/orders", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public ResponseEntity createOrder(
            @RequestBody BasicCarrierRequestDto basicCarrierRequestDto) throws Exception {

        log.info("Input is {}", basicCarrierRequestDto);

        ICarrierService carrierService
                = carrierServiceFactory.generateCarrierService(basicCarrierRequestDto.getCarrierName());

        ApiResponseDto apiResponseDto = carrierService.createOrder(basicCarrierRequestDto);

        return ResponseEntity.ok(apiResponseDto);
    }

如何生产出具有特殊需求的类

由于我们对接的快递公司不同,有的快递公司比较特别,有他们自己特殊的业务场景,需要实现特殊的方法。例如:eleme在下单之前需要对运力进行check,但是其他的物流的公司暂时没有这样的需求,如果在我们的父类接口中增加一个check运力的方法的话,对于其他的物流形来说就是一种累赘。我们这里用到了内部接口的方式来实现这个功能,因为内部接口是不需要实现类必须实现的,对于那些没有这个需求的物流方式就可以不实现这个方法,就增加了代码的美观性。

具体代码实现如下:

这是父类接口:IQueryDelivery这是方法就是check运力的方法

public interface ICarrierService {

    ApiResponseDto createOrder(BasicCarrierRequestDto basicCarrierRequestDto) throws Exception;

    interface IQueryDelivery extends ISubService {
        ApiResponseDto deliveryQuery(T t) throws Exception;
    }
}

这是实现类:

@Service
@Slf4j
public class ElemeServiceImpl implements IElemeService {

    @Override
    public ApiResponseDto createOrder(BasicCarrierRequestDto basicCarrierRequestDto){
        log.info(basicCarrierRequestDto.getCarrierName()+" create order =================");
        return ApiResponseDto.success();
    }

    @Service
    private class ElemeDeliveryQueryServiceImpl implements IQueryDelivery {

        @Override
        public ApiResponseDto deliveryQuery(DeliveryQueryDto deliveryQueryDto) throws Exception {
            log.info("Eleme is doing deliveryQuery ....");
        }
    } 
}

但是问题来了。我们的factory只会生产实现了父接口ICarrierService的类,并不会生产他们的内部类呀。不用急,我们再对factory加工一下,他就可以实现这个功能了。他的原理还是和上面的是一样的,都是利用反射来实现的。

@Override
    public  T generateSubService(Class fatherClazz, Class childClazz, String carrierName) {

        T serviceBean = null;

        if (fatherClazz.isAssignableFrom(childClazz)){

            log.debug("[{}] is one of [{}] children!", childClazz.getName(), fatherClazz.getName());
            serviceBean = (T)SpringUtils.getBean(childClazz);

        } else if (!StringUtils.isEmpty(carrierName)){

            log.debug("[{}] iterating sub implements ... ...", childClazz);
            Reflections reflections = new Reflections(this.getClass().getPackageName());
            Set> subTypes = reflections.getSubTypesOf(fatherClazz);

            log.debug("Show subTypes Set [{}]",subTypes.toString());

            for (Class subType : subTypes){

                if (subType.getSimpleName().toLowerCase().contains(carrierName.split(" ")[0])){

                    serviceBean = SpringUtils.getBean(subType);
                    log.debug("[{}] - Fetch carrierService : [{}]", childClazz, serviceBean.getClass().getName());

                }

            }

        }
        return serviceBean;
    }

调用:

@ResponseBody
    public ResponseEntity queryDelivery(
            @RequestBody DeliveryQueryDto deliveryQueryDto) throws Exception {

        ICarrierService.IQueryDelivery queryDeliveryService
                = carrierServiceFactory.generateSubService(ICarrierService.IQueryDelivery.class, CarrierServiceImpl.QueryDeliveryServiceImpl.class, null);
        ApiResponseDto apiResponseDto = queryDeliveryService.deliveryQuery(deliveryQueryDto);

        return ResponseEntity.ok(apiResponseDto);
    }
CarrierServiceImpl:
@Service("carrierQueryDelivery")
    public class QueryDeliveryServiceImpl implements IQueryDelivery {

        @Override
        public ApiResponseDto deliveryQuery(DeliveryQueryDto deliveryQueryDto) throws Exception {

            ApiResponseDto apiResponseDto = new ApiResponseDto();

            List checkAvailabilityList = new LinkedList<>();
            List list = new LinkedList<>();
            List carrierNames = deliveryQueryDto.getCarrierNames();

            for (String carrierName : carrierNames) {
                if (StringUtils.isEmpty(carrierName)) continue;
                ICarrierService carrierService = carrierServiceFactory.generateCarrierService(carrierName);
                if (null == carrierService) continue;
                Class[] declaredClasses = carrierService.getClass().getDeclaredClasses();
                List> innerClasses = Arrays.asList(declaredClasses);
                for (Class innerClass : innerClasses) {

                    IQueryDelivery queryDeliveryService
                            = carrierServiceFactory.generateSubService(IQueryDelivery.class, innerClass, null);

                    if (null == queryDeliveryService) continue;
                    apiResponseDto = queryDeliveryService.deliveryQuery(deliveryQueryDto);
                    log.info("Each carrier response :{}", apiResponseDto);
                    if (AppClientResponse.GENERAL_SUCC.getStatus().equals(apiResponseDto.getStatus())) {
                        List data = (List) apiResponseDto.getData();
                        list.addAll(data);
                    }
                }
            }
            log.info("Delivery List : {}", list);

            Map> collect
                    = list.parallelStream().collect(Collectors.groupingBy(DeliveryQueryResponseDto::getLocationNum));

            for (Map.Entry> entry : collect.entrySet()) {

                AvailabilityResultDto availabilityResultDto = new AvailabilityResultDto();
                availabilityResultDto.setLocationNum(entry.getKey());
                List carriers = new LinkedList<>();
                List valueList = entry.getValue();

                for (DeliveryQueryResponseDto deliveryDto : valueList) {

                    AvailabilityResultDto.CarrierAvailabilityDto carrierAvailabilityDto = new AvailabilityResultDto.CarrierAvailabilityDto();
                    carrierAvailabilityDto.setCarrierName(deliveryDto.getCarrierName());
                    carrierAvailabilityDto.setLeadtime(CarrierEnum.getCarrierEnum(deliveryDto.getCarrierName()).getLeadTimeHour());
                    carrierAvailabilityDto.setPrice(CarrierEnum.getCarrierEnum(deliveryDto.getCarrierName()).getSupplyPrice());
                    carriers.add(carrierAvailabilityDto);

                }
                availabilityResultDto.setCarriers(carriers);
                checkAvailabilityList.add(availabilityResultDto);
            }

            return ApiResponseDto.success(checkAvailabilityList);
        }
    }

工厂模式到这里就圆满的结束啦,在Java策略模式实战中,有针对实际需求背景对项目进行改造,使用策略者模式实现,有兴趣的同学可以看一看,有意见也欢迎大家指出。

你可能感兴趣的:(设计模式)