Solo  当前访客:0 开始使用

策略模式 从入门到落地

策略模式主要解决过多if-else导致的代码冗余情况,根据不同的条件分配到不同的策略,可以有效的减少条件判断的数量,把逻辑封装起来,使代码变得灵活。

基本使用方法

通过声明一个接口,对接口不同的实现类作为各种策略的封装。

Strategy.java

public interface Strategy {
	public void say();
}

HelloWorldStrategyImpl.java

public class HelloWorldStrategyImpl implements Strategy {
	@Override
	public void say() {
		System.out.println(“Hello Wrold!”);
	}
}

WellCh4nStrategyImpl.java

public class WellCh4nStrategyImpl implements Strategy {
	@Override
	public void say() {
		System.out.println(“WellCh4n”);
	}
}

使用的时候,通过实例化不同的实现类,来分配不同的策略。

Main.java

public static void main(String[] args) {
	Strategy helloWorldStrategy = new HelloWorldStrategyImpl();
	helloWorldStrategy.say();
	// Hello World!

	Strategy wellCh4nStrategy = new WellCh4nStrategyImpl();
	wellCh4nStrategy.say();
	// WellCh4n
}

基本使用方法2.0

结合枚举类,通过不同的枚举,分配不同的策略

StrategyImplEnum.java

public enum StrategyImplEnum {
	HELLO_WORLD(“helloWorld”, new HelloWorldStrategyImpl());
	WELL_CH4N(“wellCh4n”, new WellCh4nStrategyImpl());

	private String param;
	private Strategy impl;
	// 忽略Getter Setter

	public static Strategy getStrategyByParam(String param) {
    if (Strings.isBlank(param)) {
        return null;
    }
    for (StrategyImplEnum e ::StrategyImplEnum.class.getEnumConstants()) {
        if (type.equals(e.param)) {
            return e.impl;
        }
    }
    return null;
	}

}

Main.java

public static void main(String[] args) {
	String str = “helloWorld”;
	Strategy impl = StrategyImplEnum.getStrategyByParam(str);
	impl.say();
}

中场休息

至此我们已经实现了通过策略模式分配不同策略给业务解耦,后面如果新增业务,只需实现接口,在枚举类中增加一个类型即可。
可是请读者停下来想一下,类似于这种枚举类的容器,在我们平时开发中,是不是有类似的情况呢。
是的,Spring中的ApplicationContext就是一个类似的容器,Spring在IoC容器初始化时已经为我们实例化了我们需要的类。

Spring中策略模式使用

以SpringBoot为例,我们可以通过实现 ApplicationContextAware 复写setApplicationContext方法,给自己的类中增加一个ApplicationContext的成员变量来获取Bean。

某业务Service.java

public class xxxService implements ApplicationContextAware
 {
	private ApplicationContext context;
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.context = applicationContext;
	}

	public void do(String param) {
		Strategy s = (Strategy) context.getBean(param);
		s.say();
	}

	// 业务代码忽略
}

实际应用落地

业务需求是一个告警历史页面,有多种类型,不同的类型有不同的搜索逻辑,聚合逻辑。
我的做法是

  1. 首先我抽象一个 HistoryStrategy,不同的告警类型有不同的实现类,通过类型的字符串分配不同策略。
  2. 在枚举类中存放的是实现类的.class,而不是例子中直接存放new出来的实例,因为初始化的过程是放在Spring中做的。
  3. 通过枚举类中的一个静态方法,根据字符串遍历枚举类,获得class。
  4. 从context中通过class获得该class在IoC容器中的实例,随后调用方法。

尾巴

本例中描述的策略模式,核心是要有一个放置实例的容器,保证参数和实例之间的关系。
同样的,也可以托管一个Map在Spring中,通过KV的形式存放name和实例,也可以做到同样的效果。
再比方说需要实例不在IoC容器中,需要参数构造。而这时可以通过Class的反射实例化对象,一样可以实现效果。

本文只是提供一个思路,平时生产环境中肯定有各种复杂的情况,要根据不同情况灵活变通。


标题:策略模式 从入门到落地
作者:wellCh4n
地址:http://www.wellch4n.com/articles/2019/11/06/1573035723908.html