用策略模式优化代码的实例

实例一:

利用利用策略模式实际开发中 if else 条件判断过多的问题,条件少还好,一旦 else if 过多这里的逻辑将会比较混乱,并很容易出错。
比如:在这里插入图片描述
刚开始条件较少,也就没管那么多直接写的;现在功能多了导致每次新增一个 else 条件都得仔细核对,生怕影响之前的逻辑。

重构之后这里的结构如下:在这里插入图片描述
最后直接变为两行代码,简洁了许多。

而之前所有的实现逻辑都单独抽取到其他实现类中。在这里插入图片描述拆分后的条件执行语句在这里插入图片描述
这样每当需要新增一个 else 逻辑,只需要新增一个类实现同一个接口便可完成。每个处理逻辑都互相独立互不干扰。在这里插入图片描述
按照目前的实现画了一个草图。

整体思路如下:

定义一个 InnerCommand 接口,其中有一个 process 函数交给具体的业务实现。
根据自己的业务,会有多个类实现 InnerCommand 接口;这些实现类都会注册到 Spring Bean 容器中供之后使用。
通过客户端输入命令,从 Spring Bean 容器中获取一个 InnerCommand 实例。
执行最终的 process 函数。
主要想实现的目的就是不在有多个判断条件,只需要根据当前客户端的状态动态的获取 InnerCommand 实例。

从源码上来看最主要的就是 InnerCommandContext 类,他会根据当前客户端命令动态获取 InnerCommand 实例。在这里插入图片描述第一步是获取所有的 InnerCommand 实例列表。
根据客户端输入的命令从第一步的实例列表中获取类类型。
根据类类型从 Spring 容器中获取具体实例对象。
因此首先第一步需要维护各个命令所对应的类类型。
在这里插入图片描述所以在之前的枚举中就维护了命令和类类型的关系,只需要知道命令就能知道他的类类型。

这样才能满足只需要两行代码就能替换以前复杂的 if else,同时也能灵活扩展。

InnerCommand instance = innerCommandContext.getInstance(msg);
instance.process(msg) ;

注:如果是springboot工程在获取上下文中的bean的时候可以通过如下方式:
方法一:

新建util类,实现ApplicationContextAware,重写setApplicationContext方法。这个方式下,工具类也被注册成了Bean,既然这样,那就必须确保该类能被Spring自动扫描到。

@Component
public class SpringContextUtils implements ApplicationContextAware {
 
    private static ApplicationContext applicationContext;
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtils.applicationContext = applicationContext;
    }
 
    public static <T> T getBean(Class<T> cls) {
        if (applicationContext == null) {
            throw new RuntimeException("applicationContext注入失败");
        }
        return applicationContext.getBean(cls);
    }
 
    public static Object getBean(String name) {
        if (applicationContext == null) {
            throw new RuntimeException("applicationContext注入失败");
        }
        return applicationContext.getBean(name);
    }
 
    public static <T> T getBean(String name, Class<T> cls) {
        if (applicationContext == null) {
            throw new RuntimeException("applicationContext注入失败");
        }
        return applicationContext.getBean(name, cls);
    }
 
    public static HttpServletRequest getRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
            .getRequestAttributes();
        return requestAttributes == null ? null : requestAttributes.getRequest();
    }
}

方式二:

我想要的工具类,往往会部署在公共jar中,一般情况下不能被SpringBoot程序扫描到。所有就有手动注入上下文的方式。

@Slf4j
public class SpringUtils {
 
    private SpringUtils() {
    }
 
    private static ApplicationContext context = null;
 
    /**
     * 初始化Spring上下文
     *
     * @param ctx 上下文对象
     */
    public static void initContext(ApplicationContext ctx) {
        if(ctx == null) {
            log.warn("ApplicationContext is null.");
            return;
        }
        context = ctx;
    }
 
 
    /**
     * 根据类型获取Bean
     *
     * @param cls Bean类
     * @param <T> Bean类型
     * @return Bean对象
     */
    public static <T> T getBean(Class<T> cls) {
        return context == null ? null : context.getBean(cls);
    }
 
    /**
     * 根据名称获取Bean
     *
     * @param name Bean名称
     * @return Bean对象
     */
    public static Object getBean(String name) {
        return context == null ? null : context.getBean(name);
    }
 
    /**
     * 根据Bean名称和类获取Bean对象
     *
     * @param name Bean名称
     * @param cls Bean类
     * @param <T> Bean类型
     * @return Bean对象
     */
    public static <T> T getBean(String name, Class<T> cls) {
        return context == null ? null : context.getBean(name, cls);
    }
}

此种方式不用实现ApplicationContextAware接口,但是需要手动设置上下文。所以在程序入口处,需要调用initContext方法,完成上下文的初始化。

public static void main(String[] args) {
    ApplicationContext context = SpringApplication.run(YCloudsServiceBApplication.class, args);
    SpringUtils.initContext(context);
}

下面展示springboot实例:

实例二:(Spring Boot 工程实例)

springUtil类如上已经建好,接口ContractStrategy和其实现类MainContractStrategy和RelieveContractStrategy类似上面实例,这里不再粘出来。下面是Enum类:

public enum ContractValidateEnum {
  
  MAIN(1,MainContractStrategy.class,"主协议校验"),
  RELIEVE(2,RelieveContractStrategy.class,"解除协议校验");
  
  private int index;
  
  private Class<? extends ContractStrategy> strategy;
  
  private String info;
  
  ContractValidateEnum(int index,Class<? extends ContractStrategy> strategy, String info) {
    this.index = index;
    this.strategy = strategy;
    this.info = info;
  }
  
  /**
   * 根据index返回对应的enum
   *
   * @param index
   * @return
   */
  public static ContractValidateEnum fromIndex(int index) {
    for (ContractValidateEnum b : ContractValidateEnum.values()) {
      if (b.getIndex() == index) {
        return b;
      }
    }
    return MAIN;
  }
  
  /**
   * @return the index
   */
  public int getIndex() {
    return index;
  }


  /**
   * @return the info
   */
  public String getInfo() {
    return info;
  }

  /**
   * @return the strategy
   */
  public Class<?> getStrategy() {
    return strategy;
  }

}

然后是Context 类,他会根据当前客户端命令动态获取 strategy实例。

public class ContractValidateContext {
  
  public static ContractStrategy getStrategyInstance(ContractValidateEnum command){
    return (ContractStrategy) SpringUtil.getBean(command.getStrategy());
  }
}

这里直接在Enum里面放的Class<? extends ContractStrategy>可以简化Context里面代码,当然也可以参照实例一里面放String类型的实现类的名字,再根据Class.forName()来实现。
在使用的时候直接这样即可:

strategy = ContractValidateContext.getStrategyInstance(ContractValidateEnum.MAIN);
strategy.execValidate("参数");

注:本例主要是为了解决简单的策略模式不断新建对象造成的开销,这样用springBean配合静态Util类来管理实例,节省开销,提高系统性能,简化代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值