项目中同事的分享,保存一下(由于原文链接为内网,所以标为原创)!
说明:商旅系统是以大唐商旅购票系统为基础,用户购票后,大唐系统将机票订单(出票、改签、退票)推送至我们系统, 推送的几个订单(出票、改签、退票)同一个接口,如果按照普通的代码编写方式,会存在大量的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(推送子类型) 执行不同的业务
本文介绍了一种基于策略模式的商旅系统机票订单处理方案。通过定义接口和具体策略类,实现对不同类型的机票订单(如出票、改签、退票)的灵活处理,避免了大量if...else...带来的维护难题。
607

被折叠的 条评论
为什么被折叠?



