介绍
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。
主要是为了代码的解耦,避免每次新增策略的时候都影响到之前的策略逻辑(开闭原则),降低代码的耦合有利于后面代码的延伸(新增行为),并确保每次新增策略(功能)时都不会影响到原来的代码逻辑!
策略模式UML图 :
一、策略模式代码实现
策略接口
public interface BonusStrategy {
public void bonus();
}
策略实现
public class T1Handler implements BonusStrategy {
@Override
public void bonus() {
System.out.println("T1级别年终奖");
}
}
public class T2Handler implements BonusStrategy {
@Override
public void bonus() {
System.out.println("T2级别年终奖");
}
}
策略背景
public class BonusMapContext {
private static final Map<String, BonusStrategy> STRATEGY_MAP = new HashMap<String, BonusStrategy>();
static {
STRATEGY_MAP.put("T1", new T1Handler());
STRATEGY_MAP.put("T2", new T2Handler());
}
public static void getBonus(String staffLevel) {
BonusStrategy bonusStrategy = STRATEGY_MAP.get(staffLevel);
if (null == bonusStrategy) {
System.out.println("非正常级别!!! 无奖金");
return;
}
bonusStrategy.bonus();
}
}
这样,一个简单的策略模式就编写出来了,目前我们只有T1和T2级别的员工才有年终奖,其他级别的员工都是没有奖金的! 剥削的资本主义!!! 如果我们公司规模变大了(世界500强),需要新增一个T3级别年终奖,我们只需新增一个T3Handler类就行了,然后手动把它put到STRATEGY_MAP里去就行, 但是这样我们还是违背了’开闭原则’,为了更好的维护,此时我们将代码稍微修改一下.
实现InitializingBean接口(自动注册)
新增bonusRegister方法
public class BonusMapContext {
private static final Map<String, BonusStrategy> STRATEGY_MAP = new HashMap<String, BonusStrategy>();
public static void bonusDispatch(String staffLevel) {
BonusStrategy bonusStrategy = STRATEGY_MAP.get(staffLevel);
if (null == bonusStrategy) {
System.out.println("非正常级别!!! 无奖金");
return;
}
bonusStrategy.bonus();
}
public static void bonusRegister(String staffLevel, BonusStrategy bonusStrategy) {
if (STRATEGY_MAP.containsKey(staffLevel)) {
System.out.println("当前类型已存在!!!");
return;
}
STRATEGY_MAP.put(staffLevel, bonusStrategy);
}
}
策略类实现InitializingBean接口,并自动注册
@Component
public class T1Handler implements BonusStrategy, InitializingBean {
@Override
public void bonus() {
System.out.println("T1级别奖金");
}
@Override
public void afterPropertiesSet() throws Exception {
BonusMapContext.bonusRegister("T1", this);
}
}
@Component
public class T2Handler implements BonusStrategy, InitializingBean {
@Override
public void bonus() {
System.out.println("T2级别的奖金");
}
@Override
public void afterPropertiesSet() throws Exception {
BonusMapContext.bonusRegister("T2", this);
}
}
我们在BonusMapContext中新增了注册方法,这样子我们以后新增策略的时候,只需要让该策略实现InitializingBean接口,并自动注册即可,这样子就遵循了’开闭原则’,不需要每次都要去修改STRATEGY_MAP,但是这样子我们后面的每个策略类都要实现InitializingBean接口,这部分的逻辑是与业务完全无关的,虽然实现了自动注册,但是代码还是有冗余,需要注意的是,该实现类必须要有@Component、@Service注解,否则afterPropertiesSet方法将不起作用! 因为只有bean被初始化时,才会执行afterPropertiesSet方法!!!
基于SpringBoot实现策略模式
@Component("T1")
public class T2Handler implements BonusStrategy {
@Override
public void bonus() {
System.out.println("T2级别的奖金");
}
}
@Component("T2")
public class T1Handler implements BonusStrategy {
@Override
public void bonus() {
System.out.println("T1级别奖金");
}
}
此时代码已经编写完毕,我们新建个testController来测试一下
@Controller
public class testController {
@Autowired
private BonusMapContext context;
@ResponseBody
@RequestMapping("/test")
public void insertProduct() throws Exception {
for (int i = 0; i < 3; i++) {
context.bonusDispatch("T" + i);
}
}
}
打印结果为
需要注意的是Handler类中的value必须要是T1或者T2等自己定义的类型,否则将默认使用类名(驼峰命名)作为key,这样子我们还是会找不到对应的策略类.
经过上面代码的比较,我们会发现基于SpringBoot实现策略模式是最友好的一种方式,我们后续新增策略只需要新增Handler类,然后添加注解就可以了,SpringBoot会帮我们自动注入到上下文中的STRATEGY_MAP中,实际上在日常工作中,这样那样的需求可以用到策略模式,但是在使用设计模式的时候,我们需要同时考虑到后期的维护,如果是简单的逻辑并且后续拓展类型的可能性很小,我们可以直接使用if else,这样的代码是最简洁最实用的,如果项目中同事的水平看不懂,这也会带来问题,如果为了使用设计模式而使用,那么这将毫无价值