如何优雅的用策略模式,取代臃肿的 if-else 嵌套,看这篇就够了

1、传统的实现方式

先说一下具体的需求:公司推广入口不少,每个下单来源在下单时都作特殊的逻辑处理,可能每两天就会加一个来源。javascript

那么按照传统的实现方式代码就是以下:java

public class OrderServiceImpl implements IOrderService {
    @Override
    public String handle(OrderDTO dto) {
        String type = dto.getType();
        if ("1".equals(type)) {
            return "处理普通订单";
        } else if ("2".equals(type)) {
            return "处理团购订单";
        } else if ("3".equals(type)) {
            return "处理促销订单";
        }
        return null;
    }
}

为何非得写的这么臃肿?不少同事会说:“哎呀,没办法呀,业务催的紧,这样开发效率快省事”。的确是句大实话,不少时候业务方确实像催命鬼同样的让你赶工期,想快速实现功能,这样写是最好的选择。程序员

上边的代码看似还算清晰,可若是我告诉你公司订单来源有上百种,你想象一下那种臃肿的if-else,去翻代码时是什么感觉?面试

2、策略模式的实现方式

策略模式是oop中最著名的设计模式之一,是对方法行为的抽象,能够归类为行为设计模式,也是oopinterface经典的应用。其特色简单又实用,是我最喜欢的模式之一。算法

策略模式定义了一个拥有共同行为的算法族,每一个算法都被封装起来,能够互相替换,独立于客户端而变化。设计模式

很多人说:Java的设计模式背了不少,可平常还不就是写if-else的业务,根本就不用到。其实不是用不到是没有用到合适的位置!安全

一、策略模式的使用场景:
  • 针对同一问题的多种处理方式,仅仅是具体行为有差异时;
  • 须要安全地封装多种同一类型的操做时;
  • 同一抽象类有多个子类,而客户端须要使用if-else 或者 switch-case 来选择具体子类时。

这个是用策略模式修改后代码:ide

@Component
@OrderHandlerType(16)
public class DispatchModeProcessor extends AbstractHandler{

	@Autowired
	private OrderStencilledService orderStencilledService;
	
	@Override
	public void handle(OrderBO orderBO) {
		
		/**
    	 * 订单完结广播通知(1 - 支付完成)
    	 */
    	orderStencilledService.dispatchModeFanout(orderBO);
		
    	/**
    	 *  SCMS 出库单
    	 */
    	orderStencilledService.createScmsDeliveryOrder(orderBO.getPayOrderInfoBO().getLocalOrderNo());
	}
}

每一个订单来源都有本身单独的逻辑实现类,而每次须要添加订单来源,直接新建实现类,修改@OrderHandlerType(16)的数值便可,不再用去翻那几百行的if-lese,一劳永逸!工具

二、具体的实现过程:

一、定义一个标识订单来源的注解oop

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface OrderHandlerType {
	int value() default 0;
}

二、抽象出来一个具体的业务处理器

public abstract class AbstractHandler {
	abstract public void handle(OrderBO orderBO);
}

三、项目启动扫描 handler 入口

@Component
@SuppressWarnings({"unused","rawtypes"})
public class HandlerProcessor implements BeanFactoryPostProcessor {
	
	private String basePackage = "com.ecej.order.pipeline.processor";
	
    public static final Logger log = LoggerFactory.getLogger(HandlerProcessor.class);
	
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		
		Map<Integer,Class> map = new HashMap<Integer,Class>();
		
		ClassScaner.scan(basePackage, OrderHandlerType.class).forEach(x ->{
			int type = x.getAnnotation(OrderHandlerType.class).value();
			map.put(type,x);
		});
		
		beanFactory.registerSingleton(OrderHandlerType.class.getName(), map);
		
		log.info("处理器初始化{}", JSONObject.toJSONString(beanFactory.getBean(OrderHandlerType.class.getName())));
	}
}

四、扫描须要用到的工具类

public class ClassScaner {
	private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

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

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

	private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
	
	/**
	 * 添加包含的Fiter
	 * @param includeFilter
	 */
	public void addIncludeFilter(TypeFilter includeFilter) {
		this.includeFilters.add(includeFilter);
	}

	/**
	 * 添加排除的Fiter
	 * @param includeFilter
	 */
	public void addExcludeFilter(TypeFilter excludeFilter) {
		this.excludeFilters.add(excludeFilter);
	}
	
	/**
	 * 扫描指定的包,获取包下全部的Class
	 * @param basePackage 包名
	 * @param targetTypes 须要指定的目标类型,能够是pojo,能够是注解
	 * @return Set<Class<?>>
	 */
	public static Set<Class<?>> scan(String basePackage,
			Class<?>... targetTypes) {
		ClassScaner cs = new ClassScaner();
		for (Class<?> targetType : targetTypes){
			if(TypeUtils.isAssignable(Annotation.class, targetType)){
				cs.addIncludeFilter(new AnnotationTypeFilter((Class<? extends Annotation>) targetType));
			}else{
				cs.addIncludeFilter(new AssignableTypeFilter(targetType));
			}
		}
		return cs.doScan(basePackage);
	}
	
	/**
	 * 扫描指定的包,获取包下全部的Class
	 * @param basePackages 包名,多个
	 * @param targetTypes 须要指定的目标类型,能够是pojo,能够是注解
	 * @return Set<Class<?>>
	 */
	public static Set<Class<?>> scan(String[] basePackages,
			Class<?>... targetTypes) {
		ClassScaner cs = new ClassScaner();
		for (Class<?> targetType : targetTypes){
			if(TypeUtils.isAssignable(Annotation.class, targetType)){
				cs.addIncludeFilter(new AnnotationTypeFilter((Class<? extends Annotation>) targetType));
			}else{
				cs.addIncludeFilter(new AssignableTypeFilter(targetType));
			}
		}
		Set<Class<?>> classes = new HashSet<Class<?>>();
		for (String s : basePackages){
			classes.addAll(cs.doScan(s));
		}
		return classes;
	}
	
	/**
	 * 扫描指定的包,获取包下全部的Class
	 * @param basePackages 包名
	 * @return Set<Class<?>>
	 */
	public Set<Class<?>> doScan(String [] basePackages) {
		Set<Class<?>> classes = new HashSet<Class<?>>();
		for (String basePackage :basePackages) {
			classes.addAll(doScan(basePackage));
		}
		return classes;
	}
	
	/**
	 * 扫描指定的包,获取包下全部的Class
	 * @param basePackages 包名
	 * @return Set<Class<?>>
	 */
	public Set<Class<?>> doScan(String basePackage) {
		Set<Class<?>> classes = new HashSet<Class<?>>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
					+ 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 ignore) {}
					}
				}
			}
		} catch (IOException ex) {
			throw new RuntimeException("I/O failure during classpath scanning", ex);
		}
		return classes;
	}
	
	/**
	 * 处理 excludeFilters和includeFilters
	 * @param metadataReader
	 * @return boolean
	 * @throws IOException
	 */
	private 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;
	}
}

五、 根据类型实例化抽象类

@Component
public class HandlerContext {

	@Autowired
	private ApplicationContext beanFactory;

	public  AbstractHandler getInstance(Integer type){
		
		Map<Integer,Class> map = (Map<Integer, Class>) beanFactory.getBean(OrderHandlerType.class.getName());
		
		return (AbstractHandler)beanFactory.getBean(map.get(type));
	}
	
}

六、调用入口,我这里是接的MQ消息,会批量的处理多个订单来源

@Component
@RabbitListener(queues = "OrderPipelineQueue")
public class PipelineSubscribe{
 
	private final Logger LOGGER = LoggerFactory.getLogger(PipelineSubscribe.class);
	
	@Autowired
	private HandlerContext HandlerContext;
	
	@Autowired
	private OrderValidateService orderValidateService;
	
    @RabbitHandler
    public void subscribeMessage(MessageBean bean){
    	
    	OrderBO orderBO = JSONObject.parseObject(bean.getOrderBO(), OrderBO.class);
    	
    	if(null != orderBO &&CollectionUtils.isNotEmpty(bean.getType()))
    	{
    		for(int value:bean.getType())
    		{
       		 AbstractHandler handler = HandlerContext.getInstance(value);
       		 handler.handle(orderBO);
    		}
		}
	}
}

接收实体 MessageBean 类代码

public class MessageBean implements Serializable {
    private static final long serialVersionUID = 5454831432308782668L;
    private String cachKey;
    private List<Integer> type;
    private String orderBO;

    public MessageBean(List<Integer> type, String orderBO) {
        this.type = type;
        this.orderBO = orderBO;
    }
}

3、策略模式的优缺点

优势
  • 易于扩展,增长一个新的策略只须要添加一个具体的策略类便可,基本不须要改变原有的代码,符合开放封闭原则
  • 避免使用多重条件选择语句,充分体现面向对象设计思想 策略类之间能够自由切换,因为策略类都实现同一个接口,因此使它们之间能够自由切换
  • 每一个策略类使用一个策略类,符合单一职责原则 客户端与策略算法解耦,二者都依赖于抽象策略接口,符合依赖反转原则
  • 客户端不须要知道都有哪些策略类,符合最小知识原则
缺点
  • 策略模式,当策略算法太多时,会形成不少的策略类
  • 客户端不知道有哪些策略类,不能决定使用哪一个策略类,这点能够经过封装common公共包解决,也能够考虑使IOC容器依赖注入的方式来解决。

如下是订单来源策略类的一部分,不得不说策略类确实比较多。 在这里插入图片描述

总结:

凡事都有他的两面性,if-else多层嵌套和也都有其各自的优缺点:

if-else的有点就是简单,想快速迭代功能,逻辑嵌套少且不会持续增长,if-else更好些,缺点也是显而易见,代码臃肿繁琐不便于维护。

策略模式 将各个场景的逻辑剥离出来维护,同一抽象类有多个子类,须要使用if-else 或者 switch-case 来选择具体子类时,建议选策略模式,他的缺点就是会产生比较多的策略类文件。

两种实现方式各有利弊,如何选择仍是要依据具体业务场景,仍是那句话设计模式不是为了用而用,必定要用在最合适的位置。

整理了几百本各种技术电子书,有须要的同窗能够,在我同名公众号回复[ 666 ]自取。技术群快满了,想进的同窗能够加我好友,和大佬们一块儿吹吹技术,期待你的加入。

在这里插入图片描述

不管你是刚入行、仍是已经有几年经验的程序员,相信这份面试提纲都会给你很多助力,长按二维码关注 『 程序员内点事 』 ,回复 『 offer 』 自行领取,祝你们 offer 拿到手软

在这里插入图片描述

相关文章
相关标签/搜索