最近在做一个物流配送的项目,对接了很多的快递公司,其中有一部分的业务逻辑是这样的:
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 extends ICarrierService> subType : subTypeList){
if (subType.getName().toLowerCase().contains(carrierName.split(" ")[0])){
carrierService = SpringUtils.getBean(subType);
log.debug("[{}] - Fetch carrierService : [{}]", carrierName, carrierService.getClass().getName());
}
}
return carrierService;
}
}
项目的包结构如下:
父类接口:
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 extends T> 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策略模式实战中,有针对实际需求背景对项目进行改造,使用策略者模式实现,有兴趣的同学可以看一看,有意见也欢迎大家指出。