java中用策略模式替代if-else的大段逻辑
问题:
java本来是一门以面向对象为主的语言,但很多人嘴上说着java面向对象,然后除了表映射实体之外,其他的还是面向过程的思路。
就比如今天要说的,代码中大段大段的if-else判断,每个if-else代码块中又有很多复杂的逻辑,导致整个代码混乱不堪,让别人看到就感觉看到屎一样的代码一样。
那么,如何优雅的替代这些代码呢,其实有个设计模式(策略模式)就很好的解决了这个问题。
情景举例:
比如说订单处理,订单会有多个type状态,比如说
type=1的时候,就执行普通订单的逻辑;
type=2的时候,就执行满减促销订单的逻辑;
type=3的时候,就执行满返促销订单的逻辑;
等等,可能type会有数十种甚至更多种情况。
然后有些人就会开始if-else了,比如有如下的伪代码:
if(type=1){普通订单...(此处省略100多行处理逻辑)}else if(type=2){满减促销订单...(此处省略100多行处理逻辑)}else if(type=3){满返促销订单...(此处省略100多行处理逻辑)}else if(type=n){...(此处省略几百上千行的逻辑)}
做的好点的,会把if-else代码块中的逻辑,抽成一个个的方法,会稍微显的代码清晰许多,但这些都是面向过程的思想。
我认为,这种情况就应该用以下这种方式,即,用策略模式代替if-else,真正做到面向对象。
把每种不同类型的订单抽成一个个对象,然后通过不同的注解标识来区分调用。
用策略模式代替if-else:
首先,本次例子用的是Spring-Boot框架,亲测没问题。SpringMVC框架应该也是没问题的。
定义一个订单类,里面有type属性,type可以是"1"、“2”、“3”…
定义一个抽象类AbstractHandler,里面有个抽象方法handle,入参是订单类
定义一个注解HandlerType,有个value属性,value是几就代表这个注解注的这个类是什么类型的订单
定义普通类HandlerImpl01,实现AbstractHandler,代表普通订单,即@HandlerType(“1”);
定义普通类HandlerImpl02,实现AbstractHandler,代表满减促销订单,即@HandlerType(“2”);
定义普通类HandlerImpl03,实现AbstractHandler,代表满返促销订单,即@HandlerType(“3”);
定义一个初始化类HandlerProcessor,实现BeanFactoryPostProcessor,过程如下:
1、找到带有注解@HandlerType的类,
2、以注解的值为key,对应的类为value,存在一个map中
3、将这个map作为构造函数的参数,初始化HandlerContext,将HandlerContext注册到spring中成为一个单例bean。
很明显,目的就是为了保存不同type对应的不同类。
定义类HandlerContext,有个map类型的属性叫handlerMap,有个getInstance的方法,入参是type,返回AbstractHandler。
最后使用的时候,是先调用handlerContext.getInstance方法,根据type获取对应的AbstractHandler。
然后再调用他的handle方法,执行对应订单类型的处理逻辑。
具体代码如下:
//@Data是lombok的注解,为了省略不写get/set方法@Datapublic class OrderDTO {private String code;private BigDecimal price;/*** 订单类型* 1:普通订单* 2:满减订单* 3:满返订单*/private String type;}
public abstract class AbstractHandler {abstract public String handle(OrderDTO orderDTO);}
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic @interface HandlerType {String value();}
@Component@HandlerType("1")public class HandlerImpl01 extends AbstractHandler {@Overridepublic String handle(OrderDTO orderDTO) {System.out.println("处理type为1的订单,orderDTO.type="+orderDTO.getType());return "success";}}
@Component@HandlerType("2")public class HandlerImpl02 extends AbstractHandler {@Overridepublic String handle(OrderDTO orderDTO) {System.out.println("处理type为2的订单,orderDTO.type="+orderDTO.getType());return "success";}}
@Component@HandlerType("3")public class HandlerImpl03 extends AbstractHandler {@Overridepublic String handle(OrderDTO orderDTO) {System.out.println("处理type为3的订单,orderDTO.type="+orderDTO.getType());return "success";}}
@Component@SuppressWarnings("unchecked")public class HandlerProcessor implements BeanFactoryPostProcessor {//这里是具体的handler策略类的包的位置,为了后面的包扫描private static final String HANDLER_PACKAGE = "com.zs.handler";@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {Map<String, Class> handlerMap = new HashMap<>();//包扫描ClassScaner.scan(HANDLER_PACKAGE,HandlerType.class).forEach(clazz ->{Annotation annotation = clazz.getAnnotation(HandlerType.class);HandlerType handlerType = (HandlerType) annotation;String type = handlerType.value();System.out.println(type);handlerMap.put(type,clazz);});HandlerContext handlerContext = new HandlerContext(handlerMap);//注册单例beanFactory.registerSingleton(HandlerContext.class.getName(),handlerContext);}}
public class HandlerContext {private Map<String,Class> handlerMap;public HandlerContext(Map<String, Class> handlerMap) {this.handlerMap = handlerMap;}public AbstractHandler getInstance(String type){Class clazz = handlerMap.get(type);if(clazz == null){throw new IllegalArgumentException("没有type对应的处理器,type:"+type);}return (AbstractHandler)SpringContextUtils.getBean(clazz);}}
定义接口:
public interface OrderService {/*** 根据订单类型处理订单* @param orderDTO* @return*/String handle(OrderDTO orderDTO);}
实现接口:
@Servicepublic class OrderServiceImpl implements OrderService {@Resourceprivate HandlerContext handlerContext;@Overridepublic String handle(OrderDTO orderDTO) {System.out.println("OrderServiceImpl handle 方法开始执行===");AbstractHandler handler = handlerContext.getInstance(orderDTO.getType());return handler.handle(orderDTO);}}
包扫描的工具类:
public class ClassScaner implements ResourceLoaderAware {private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);public ClassScaner() {}public void setResourceLoader(ResourceLoader resourceLoader) {this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);this.metadataReaderFactory = new CachingMetadataReaderFactory( resourceLoader);}// public final ResourceLoader getResourceLoader() {// return this.resourcePatternResolver;// }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 static Set<Class> scan(String basePackage, Class<? extends Annotation>... annotations) {ClassScaner cs = new ClassScaner();for (Class anno : annotations)cs.addIncludeFilter(new AnnotationTypeFilter(anno));return cs.doScan(basePackage);}// public static Set<Class> scan(String[] basePackages, Class<? extends Annotation>... annotations) {// ClassScaner cs = new ClassScaner();// for (Class anno : annotations)// cs.addIncludeFilter(new AnnotationTypeFilter(anno));// Set<Class> classes = new HashSet<Class>();// for (String s : basePackages)// classes.addAll(cs.doScan(s));// return classes;// }public Set<Class> doScan(String basePackage) {Set<Class> classes = new HashSet<Class>();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)|| 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;}}
spring工具类,为了获取bean
@Componentpublic class SpringContextUtils implements ApplicationContextAware {private static ApplicationContext applicationContext = null;public static ApplicationContext getApplicationContext(){return applicationContext;}@SuppressWarnings("unchecked")public static <T> T getBean(String beanId) {return (T) applicationContext.getBean(beanId);}public static <T> T getBean(Class<T> requiredType) {return (T) applicationContext.getBean(requiredType);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringContextUtils.applicationContext = applicationContext;}}