注解+策略模式+工厂模式代替if...else...

项目中同事的分享,保存一下(由于原文链接为内网,所以标为原创)!

说明:商旅系统是以大唐商旅购票系统为基础,用户购票后,大唐系统将机票订单(出票、改签、退票)推送至我们系统, 推送的几个订单(出票、改签、退票)同一个接口,如果按照普通的代码编写方式,会存在大量的if...else....  ,如果后续需要改动某个业务逻辑,出票、改签、退票都会容易相互影响。为了解决该问题,引入策略模式编写代码

1.创建实体类、服务层、控制层

这里方便起见再写一边,具体内容有差别,但是实现功能不变。

 

 

@Data
public class PushOrderBaseDto<T> {

    /**
 * 推送类型
 */
 @NotNull(message = "推送类型不能为空")
    private Integer notifyType;

    /**
 * 推送子类型
 */
 @NotNull(message = "推送子类型不能为空")
    private Integer subNotifyType;

    /**
 * 推送时间戳
 */
 private long notifyTime;

    /**
 * 推送签名
 */
 private String sign;

    /**
 * 是否加密
 */
 private Boolean isEncrypt;

    /**
 * 推送唯一秘钥
 */
 private String soleKey;

    /**
 * 推送数据主体
 */
 @NotNull(message = "推送数据主体不能为空")
    @JsonDeserialize(using = ObjectJsonDeserializer.class)
    private NotifyDataDto<T> notifyData;


}

 

 

 

@RestController
@RequestMapping("/airplaneTicketOrder")
public class AirplaneTicketOrderController {


 @Autowired
 private IAirplaneTicketOrderService iAirplaneTicketOrderService;

    /**
 * 推送机票信息
 *
 * @param dto 审批数据id
 * @return LiteRestResponse
 * @author yufangze. create time: 2020-10-13
 */
 @PostMapping("/push")
    public DatangPushResponseVo push(@Valid @RequestBody PushOrderBaseDto<Object> dto) {
        iAirplaneTicketOrderService.push(dto);
        return DatangPushResponseVo.success();
    }

}

 

@Slf4j
@Service
public class AirplaneTicketOrderServiceImpl extends ServiceImpl<AirplaneTicketOrderMapper, AirplaneTicketOrder> implements IAirplaneTicketOrderService {
 @Autowired(required = false)
 private HandlerContext handlerContext;
/**
 * 推送机票订单
 *
 * @param pushOrderBaseDto 审批数据
 * @return LiteRestResponse
 * @author yufangze. create time: 2020-10-13
 */
@Override
public void push(PushOrderBaseDto<Object> pushOrderBaseDto) {
    log.info("【推送机票订单】推送内容:{}", JSONObject.toJSONString(pushOrderBaseDto));
    AirplaneTicketOrderStrategy strategy = handlerContext.getInstance(AirplaneOrderSeatPushEnums
 .getName(pushOrderBaseDto.getSubNotifyType()));
    strategy.push(pushOrderBaseDto.getNotifyData());
}
}

 

2、创建策略接口 策略实现类

 

/**
 * @description: 机票订单推送策略接口
 * @author: yufangze
 * @create: 2020-10-13 16:35
 */
public interface AirplaneTicketOrderStrategy {
    /**
 * 机票订单推送策略接口
 *
 * @param notifyDataDto 推送主体数据
 * @author yufangze. create time: 2020-10-13
 */
 void push(NotifyDataDto<Object> notifyDataDto);
}

 

2.1 机票订单出票推送

 

@OrderPushType(type = "airplaneIssueTicket")
@Component
@Slf4j
public class AirplaneIssueTicketStrategy implements AirplaneTicketOrderStrategy {
/**
 * 机票订单出票推送
 *
 * @param
 * @return
 * @author yufangze. create time: 2020-10-13
 */
@Override
public void push(NotifyDataDto<Object> notifyDataDto) {
}
}

 

2.2 机票订单退票推送

 

@OrderPushType(type = "airplaneRefundTicket")
@Component
@Slf4j
public class AirplaneRefundTicketStrategy implements AirplaneTicketOrderStrategy {
/**
 * 机票订单退票推送
 *
 * @param
 * @return
 * @author yufangze. create time: 2020-10-13
 */
@Override
public void push(NotifyDataDto<Object> notifyDataDto) {
}

}

 

2.3推送机票订单改签

 

@OrderPushType(type = "airplaneChange")
@Component
@Slf4j
public class AirplaneChangeStrategy implements AirplaneTicketOrderStrategy {
/**
 * 机票订单改签推送
 *
 * @param
 * @return
 * @author yufangze. create time: 2020-10-13
 */
@Override
public void push(NotifyDataDto<Object> notifyDataDto) {
}
}

 

3、订单推送类型注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface OrderPushType {

    /**
 * 机票订单推送类型
 * 
 * @return
 * @author yufangze. create time: 2020-10-13
 */
 String type();
    
}

 

4、订单推送类型注解

 

@Component
public class HandlerProcessor implements BeanFactoryPostProcessor {

    private static final String STRATEGY_PACKAGE = "com.runlion.middleground.businesstravel.strategy";

    /**
 * 扫描@OrderPushType,初始化HandlerContext,将其注册到spring容器
 *
 * @param beanFactory bean工厂
 * @throws BeansException
 */
 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Map<String, Class> handlerMap = Maps.newHashMapWithExpectedSize(3);
        ClassScanner.scan(STRATEGY_PACKAGE, OrderPushType.class).forEach(clazz -> {
            //获取注解中的类型值
 String type = clazz.getAnnotation(OrderPushType.class).type();
            //将注解中的类型值作为key,对应的类作为value,保存在map中
 handlerMap.put(type, clazz);
        });
        HandlerContext context = new HandlerContext(handlerMap);
        beanFactory.registerSingleton(HandlerContext.class.getName(), context);
    }
}

 

5、机票订单处理器上下文

 

public class HandlerContext {

    private static final Logger logger = LoggerFactory.getLogger(HandlerContext.class);

    private Map<String, Class> handlerMap;

    public HandlerContext(Map<String, Class> handlerMap) {
        this.handlerMap = handlerMap;
    }

    public AirplaneTicketOrderStrategy getInstance(String type) {
        Class clazz = handlerMap.get(type);

        if (clazz == null) {
            logger.error("没有type为‘" + type + "’的策略处理器");
            throw new IllegalArgumentException("not found strategy handler for type: " + type);

        }
        return (AirplaneTicketOrderStrategy) BeanTool.getBean(clazz);
    }
}

 

5、Bean工具类

 

@Component
public class BeanTool implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
 public void setApplicationContext(ApplicationContext context) throws BeansException {
        if (applicationContext == null) {
            applicationContext = context;
        }
    }

    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
}

 

6、机票订单策略类扫描

public class ClassScanner implements ResourceLoaderAware {

    private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();
    private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();

    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);

    @SafeVarargs
 public static Set<Class<?>> scan(String[] basePackages, Class<? extends Annotation>... annotations) {
        ClassScanner cs = new ClassScanner();

        if (ArrayUtils.isNotEmpty(annotations)) {
            for (Class anno : annotations) {
                cs.addIncludeFilter(new AnnotationTypeFilter(anno));
            }
        }

        Set<Class<?>> classes = new HashSet<>();
        for (String s : basePackages) {
            classes.addAll(cs.doScan(s));
        }

        return classes;
    }

    @SafeVarargs
 public static Set<Class<?>> scan(String basePackages, Class<? extends Annotation>... annotations) {
        return ClassScanner.scan(StringUtils.tokenizeToStringArray(basePackages, ",; \t\n"), annotations);
    }

    public final ResourceLoader getResourceLoader() {
        return this.resourcePatternResolver;
    }

    @Override
 public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils
 .getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(
                resourceLoader);
    }

    public void addIncludeFilter(TypeFilter includeFilter) {
        this.includeFilters.add(includeFilter);
    }

    public void addExcludeFilter(TypeFilter excludeFilter) {
        this.excludeFilters.add(0, excludeFilter);
    }

    public void resetFilters(boolean useDefaultFilters) {
        this.includeFilters.clear();
        this.excludeFilters.clear();
    }

    public Set<Class<?>> doScan(String basePackage) {
        Set<Class<?>> classes = new HashSet<>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
 + org.springframework.util.ClassUtils
 .convertClassNameToResourcePath(SystemPropertyUtils
 .resolvePlaceholders(basePackage))
                    + "/**/*.class";
            Resource[] resources = this.resourcePatternResolver
 .getResources(packageSearchPath);

            for (int i = 0; i < resources.length; i++) {
                Resource resource = resources[i];
                if (resource.isReadable()) {
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    if ((includeFilters.size() == 0 && excludeFilters.size() == 0) ) {
                        try {
                            classes.add(Class.forName(metadataReader
 .getClassMetadata().getClassName()));
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                   else if (this.matches(metadataReader)){
                        try {
                            classes.add(Class.forName(metadataReader
 .getClassMetadata().getClassName()));
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "I/O failure during classpath scanning", ex);
        }
        return classes;
    }

    protected boolean matches(MetadataReader metadataReader) throws IOException {
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return false;
            }
        }
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return true;
            }
        }
        return false;
    }
}

完成。

 

最后调用推送接口,根据subNotifyType(推送子类型) 执行不同的业务

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值