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

经常听同事抱怨,订单来源又加了一种,代码又要加一层if-else判断,光判断订单来源的if-else就好几百行代码,代码我都不想看了,相信很多同行都有过这样的感受!

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

二、策略模式的实现方式

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

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

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

策略模式的使用场景:

  • 针对同一问题的多种处理方式,仅仅是具体行为有差别时;
  • 需要安全地封装多种同一类型的操作时;
  • 同一抽象类有多个子类,而客户端需要使用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,一劳永逸!

具体的实现过程:

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 或者 switch-case 来选择具体子类时,建议选策略模式;缺点:策略类文件太多

两种实现方式各有利弊,选择的时候还是要依据具体业务,还是那句话设计模式不是为了用而用,要有一个合适应用场景。



关注微信公众号:【程序员内点事】,免费获取2000学习资料,内含精选面试题,SSM、Spring全家桶、微服务、MySQL、MyCat、集群、分布式、中间件、Linux、网络、多线程,Jenkins、Nexus、Docker、ELK等等免费学习视频,持续更新!

原文地址:https://www.cnblogs.com/chengxy-nds/p/12205104.html

时间: 2024-08-30 00:34:12

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

小酌重构系列[15]&mdash;&mdash;策略模式代替分支

前言 在一些较为复杂的业务中,客户端需要依据条件,执行相应的行为或算法.在实现这些业务时,我们可能会使用较多的分支语句(switch case或if else语句).使用分支语句,意味着"变化"和"重复",每个分支条件都代表一个变化,每个分支逻辑都是相似行为或算法的重复.当追加新的条件时,我们需要追加分支语句,并追加相应的行为或算法. 上一篇文章"使用多态代替条件判断"中,我们讲到它可以处理这些"变化"和"重复&qu

初识策略模式

首先谈谈自己对策略模式的理解: 假如业务需求中需要根据不同的任务种类(假设A B等等)作出不同的处理方式,我们是否要采用if else的方式,逐个判断呢? if(type=="A"){ //A的处理逻辑 }else if(type=="B"){ //B的处理逻辑 }//........ 以上写法实现功能自然没有问题,但是随着任务种类的增加,我们需要不停的添加或者修改if else判断语句,以及添加或修改相应的处理逻辑,方法也会非常臃肿,亦不符合面向对象的对修改关闭,

(七)策略模式详解

上章我们着重讲解了观察者模式和事件驱动,那么本章来讨论一个个人认为在开发过程中出场率极高的设计模式,策略模式. 策略模式在LZ第一次接触到的时候,LZ是这么理解的,就是如果我们想往一个方法当中插入随便一段代码的话,就是策略模式.即如下形式. [java] view plaincopy <EMBED id=ZeroClipboardMovie_1 name=ZeroClipboardMovie_1 type=application/x-shockwave-flash align=middle pl

Javascript设计模式与开发实践详解(二:策略模式) http://www.jianshu.com/p/ef53781f6ef2

上一章我们介绍了单例模式及JavaScript惰性单例模式应用这一次我主要介绍策略模式策略模式是定义一系列的算法,把它们一个个封装起来,并且让他们可以互相替换.比方说在现实中很多时候也有很多途径到达同一个目的地,比如我们去某个地方旅游,可以选择坐飞机,乘火车,骑自行车等方式. 使用策略模式计算奖金 很多公司的年终奖是根据员工的工资基数和年底绩效来发放的.例如,绩效为 S 的人年终奖有4倍工资,绩效为 A 的人年终奖有3倍工资,绩效为 B 的人年终奖有2倍工资.现在我们来计算员工的年终奖. var

命令模式与策略模式之己见

以前项目写过关于TR069协议报文处理的代码(主要是基于SOAP协议发送一些远程命令并处理响应),在设计的时候,想的是应用策略模式对报文进行解析处理, 下图是主要代码结构(和策略模式很像) 代码类似于: /** * 1.需要解析的XML */ String xml = "<xml>...</xml>"; /** * 2.获取xml类型 */ MessageType type = SoapMessageFactory.getRpcType(xml); /** *

记一场精彩的篮球比赛——浅谈策略模式

策略模式 声明:本文为原创,如有转载请注明转载与原作者并提供原文链接,仅为学习交流,本人才识短浅,如有错误,敬请指正 虽然我本人比较讨厌一些很官方的术语定义,因为我经常弄不明白有些定义讲了个啥,但是为了让这篇博文显得不那么轻浮,所以我也就不能免俗的先将设计模式之策略模式的定义首先丢到各位看官面前. 策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户. 第一眼这个定义看上去,难免会让人心生胆怯,又是算法族,又是封装,又是替换,似乎很复杂的样子,但是实

利用策略模式消除分支

在实际的开发过程中,我们经常会遇到对于不同的对象采用不同的算法或者策略的场景. 一个真实的例子是这样的: 假设现在要将一个Student对象存入数据库.在逻辑层,需要对对象的字段进行合法性判断,比如ID是否超过某个阈值,名字长度是否超长. 1 public class Student { 2 3 private Integer id; 4 5 private String name; 6 7 public Integer getId() { 8 return id; 9 } 10 11 publ

设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也经常遇到类似的情况,实现某一个功能有多种算法或者策略,我们能够依据环境或者条件的不同选择不同的算法或者策略来完毕该功能.如查找.排序等,一种经常使用的方法是硬编码(Hard Coding)在一个类中,如须要提供多种查找算法,能够将这些算法写到一个类中,在该类中提供多个方法,每个方法相应一个详细的查找算法:当然也能够将这些查找算法封装在一个统一的方法中,通过if-else-或者case等条件推断语句来进行选择.

理解js设计模式之策略模式

策略模式的定义:定义一系列的算法,然后根据实际情况去调用 一个小插曲:最近在项目的过程中接手了一个比较复杂的需求,由于是旧的项目用新的框架重构,所以能够看见以前的代码,吸取下前人代码的精华,复用一些可用的代码,免得自己写半天..当然这篇的主题是策略模式,不会离题,因为当我完成了Version 1 后,项目里面大量的if-else 字段的验证都放在一个函数里面,而且不同字段有些还会相互影响,导致代码很长很乱大概有几十行的if else ,丝毫没有条理:这个时候,需要去重构这个原本的底层验证函数,那