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

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

一、传统的实现方式

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

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

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,去翻代码时是什么感受?

二、策略模式的实现方式

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

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

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

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

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

@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,一劳永逸!

2、具体的实现过程:

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

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

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

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

3、项目启动扫描 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())));
    }
}

4、扫描需要用到的工具类

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;
    }
}

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


@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));
    }
    
}

6、调用入口,我这里是接的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;
    }
}

三、策略模式的优缺点

优点
  • 易于扩展,增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合开放封闭原则

    • 避免使用多重条件选择语句,充分体现面向对象设计思想 策略类之间可以自由切换,由于策略类都实现同一个接口,所以使它们之间可以自由切换

      • 每个策略类使用一个策略类,符合单一职责原则 客户端与策略算法解耦,两者都依赖于抽象策略接口,符合依赖反转原则
    • 客户端不需要知道都有哪些策略类,符合最小知识原则
缺点
  • 策略模式,当策略算法太多时,会造成很多的策略类
  • 客户端不知道有哪些策略类,不能决定使用哪个策略类,这点可以通过封装common公共包解决,也可以考虑使IOC容器依赖注入的方式来解决。

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

总结:

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

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

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

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


今天就说这么多,如果本文对您有一点帮助,希望能得到您一个点赞👍哦

您的认可才是我写作的动力!

相关文章
|
6月前
|
设计模式 API 数据安全/隐私保护
探索设计模式的魅力:外观模式简化术-隐藏复杂性,提供简洁接口的设计秘密
外观模式是一种关键的设计模式,旨在通过提供一个简洁的接口来简化复杂子系统的访问。其核心价值在于将复杂的内部实现细节封装起来,仅通过一个统一的外观对象与客户端交互,从而降低了系统的使用难度和耦合度。在软件开发中,外观模式的重要性不言而喻。它不仅能够提高代码的可读性、可维护性和可扩展性,还能促进团队间的协作和沟通。此外,随着业务需求和技术的发展,外观模式能够适应变化,通过修改外观对象来灵活调整客户端与子系统之间的交互方式。总之,外观模式在软件设计中扮演着举足轻重的角色,是构建高效、稳定且易于维护的软件系统的关键
175 1
探索设计模式的魅力:外观模式简化术-隐藏复杂性,提供简洁接口的设计秘密
|
6月前
|
设计模式
二十三种设计模式全面解析-装饰器模式-超越继承的灵活装扮
二十三种设计模式全面解析-装饰器模式-超越继承的灵活装扮
|
设计模式 算法 Java
设计模式第十五讲:重构 - 改善既有代码的设计(下)
设计模式第十五讲:重构 - 改善既有代码的设计
284 0
|
4月前
|
编译器 程序员 C++
【C++高阶】掌握C++多态:探索代码的动态之美
【C++高阶】掌握C++多态:探索代码的动态之美
43 0
|
Go 开发者
一文详解Go语言接口嵌套组合的精髓!
一文详解Go语言接口嵌套组合的精髓!
185 0
|
6月前
针对抽象编程与对应的好处
针对抽象编程与对应的好处
49 1
|
6月前
|
设计模式 算法 Java
二十三种设计模式全面解析-当你的代码需要多种算法时,策略模式是你的救星!
二十三种设计模式全面解析-当你的代码需要多种算法时,策略模式是你的救星!
|
6月前
|
设计模式 存储 缓存
二十三种设计模式全面解析-探索解释器模式的高级应用和优化技巧:解锁代码解析的新境界
二十三种设计模式全面解析-探索解释器模式的高级应用和优化技巧:解锁代码解析的新境界
|
6月前
|
设计模式 算法
二十三种设计模式全面解析-迭代器模式进阶篇:探索变体与扩展
二十三种设计模式全面解析-迭代器模式进阶篇:探索变体与扩展
|
6月前
|
设计模式 存储
二十三种设计模式全面解析-深入探究备忘录模式:保留过去,预见未来
二十三种设计模式全面解析-深入探究备忘录模式:保留过去,预见未来