• 在SpringBoot中实现策略模式


    首先定义一个Strategy接口来表示一个策略:

    public interface Strategy {
        String flag();
        void process();
    }
    

    其中flag方法返回当前策略的唯一标识,process则是该策略的具体执行逻辑。

    下面是Strategy接口的两个实现类:

    public class StrategyImpl1 implements Strategy {
        @Override
        public String flag() {
            return "s1";
        }
    
        @Override
        public void process() {
            System.out.println("strategy 1");
        }
    }
    
    public class StrategyImpl2 implements Strategy {
        @Override
        public String flag() {
            return "s2";
        }
    
        @Override
        public void process() {
            System.out.println("strategy 2");
        }
    }
    

    然后定义一个StrategyRunner接口用来表示策略的调度器:

    public interface StrategyRunner {
        void run(String flag);
    }
    

    run方法内部通过判断flag的值来决定具体执行哪一个策略。

    下面是一个简单的StrategyRunner

    public class StrategyRunnerImpl implements StrategyRunner {
        private static final List<Strategy> STRATEGIES = Arrays.asList(new StrategyImpl1(), new StrategyImpl2());
        private static final Map<String, Strategy> STRATEGY_MAP;
    
        static {
            STRATEGY_MAP = STRATEGIES.stream()
                    .collect(Collectors.toMap(Strategy::flag, s -> s));
        }
    
        @Override
        public void run(String flag) {
            STRATEGY_MAP.get(flag).process();
        }
    }
    

    StrategyRunnerImpl内部,定义了一个STRATEGIES列表来保存所有Strategy实现类的实例,以及一个叫做STRATEGY_MAPMap来保存flagStrategy实例之间的对应关系,static块中的代码用于从STRATEGIES列表构造STRATEGY_MAP。这样,在run方法中就可以很方便地获取到指定flagStrategy实例。

    这个实现虽然简单,但是它有个很大的缺点,想象一下,如果我们想添加新的Strategy实现类,我们不仅需要添加新的实现类,还要修改STRATEGIES列表的定义。这样就违反了“对扩展开放,对修改关闭”的原则。

    借助于Spring的IOC容器和SpringBoot的自动配置,我们可以以一种更加优雅的方式实现上述策略模式。

    首先,我们继续使用StrategyImpl1StrategyImpl2这两个实现类。不过,为了将它们注册进Spring的IOC容器,需要给他们标注上Component注解:

    @Component
    public class StrategyImpl1 implements Strategy {
        ...
    }
    
    @Component
    public class StrategyImpl2 implements Strategy {
        ...
    }
    

    然后,写一个StrategyConfig配置类,用于向容器中注册一个StrategyRunner

    @Configuration
    public class StrategyConfig {
        @Bean
        public StrategyRunner strategyRunner(List<Strategy> strategies) {
            Map<String, Strategy> strategyMap = strategies.stream()
                    .collect(Collectors.toMap(Strategy::flag, s -> s));
            return flag -> strategyMap.get(flag).process();
        }
    }
    

    仔细看strategyRunner方法的实现,不难发现,其中的逻辑与之前的StrategyRunnerImpl几乎完全相同,也是根据一个List<Strategy>来构造一个Map<String, Strategy>。只不过,这里的strategies列表不是我们自己构造的,而是通过方法参数传进来的。由于strategyRunner标注了Bean注解,因此参数上的List<Strategy>实际上是在SpringBoot初始化过程中从容器获取的(还记得之前我们把两个Strategy的实现类注册进了容器吗)。

    这样,我们再也无需操心系统中一共有多少个Strategy实现类,因为SpringBoot的自动配置会帮我们把它们全部收集起来。我们只需编写自己的Strategy实现类,然后将它注册进容器,并在任何需要的地方注入StrategyRunner

    @Autowired
    private StrategyRunner strategyRunner;
    

    然后直接使用strategyRunner就行了:

    strategyRunner.run("s1");
    strategyRunner.run("s2");
    

    控制台输出如下:

    strategy 1
    strategy 2
    

    也就是说,当我们想添加新的Strategy实现类时,我们只需添加新的代码,而无需修改任何现有的代码,这样就完美地实现了“对扩展开放,对修改关闭”的目标。

  • 相关阅读:
    SQLServer 高可用、高性能和高保护延伸
    SQLServer 通过DMV实现低影响的自动监控和历史场景追溯
    查找表包含的页和页所在的表
    出身在二三线城市软件工作者的悲哀
    SQL语句实现取消自增列属性
    基于Apache(without ssl)的svn环境搭建
    sqlite3 命令行操作
    HTML常用特殊符号集
    IOS项目目录结构和开发流程
    Mac OSX 快捷键&命令行
  • 原文地址:https://www.cnblogs.com/baiyuxuan/p/14873619.html
Copyright © 2020-2023  润新知