策略模式消除恶心的if

一 . 什么是策略模式

策略模式是对算法的包装,把使用算法的责任和算法本身分隔开,委派给不同的对象管理。策略模式通常把一系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。

简单来说就是就定义一个策略接口,子类策略去实现该接口去定义不同的策略。然后定义一个环境(Context)类,以策略接口作为成员变量,根据环境来使用具体的策略。

MPrl0e.png

二. 使用场景

​ 业务中有要根据不同条件来使用不同算法的情况下,并且这些算法可以抽象的,就可以去使用。例如说,根据用户的分类,可以分为普通用户、白金用户、钻石用户等,不同种类的用户结算时需要不同的结算算法。如果不使用的话,那么可能造成每增加用户种类就需要去,就要在业务代码上去增加一个判断,造成的后果就是代码越来越冗余,不利于维护,也不方便阅读。

三. 如何使用

1.定义策略接口

1
2
3
4
public interface Strategy {

public Double count(Double pay);
}

2.不同策略实现该策略接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

public class StrategyNormal implements Strategy {

@Override
public Double count(Double pay) {
return pay ;
}
}

//白金会员98折
public class StrstegyPlatinum implements Strategy {
@Override
public Double count(Double pay) {
return pay * 0.98;
}
}

//钻石会员9折

public class StrstegyDiamond implements Strategy {
@Override
public Double count(Double pay) {
return pay * 0.9;
}
}

3.定义策略环境

1
2
3
4
5
6
7
8
9
10
11
12
public class Context {

private Strategy strategy;

public Context(Strategy strategy) {
this.strategy = strategy;
}


public Double count(Double pay){
return strategy.count(pay);
}

4.定义策略工厂

为什么有策略工厂?因为在策略环境Context 中需要具体的策略实现类去进行算法计算的,所以需要根据客户端给出的条件去具体的拿到策略类。

首先我们定义一个枚举类,定义不同的用户级别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public enum CustomerType {

NORMAL(0,"普通用户"),

PLATINUM(1,"白金用户"),

DUAMOND(2,"钻石用户");

private int index;

private String message;

CustomerType(int index, String message) {
this.index = index;
this.message = message;
}

@Override
public String toString() {
return "CustomerType{" +
"index=" + this.index +
", message='" + this.message + ''' +
'}';
}

定义具体的策略工厂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class StrategyFactory {
private static Map<CustomerType, Strategy> strstegyMap = new HashMap<CustomerType,Strategy>();

static {
strstegyMap.put(CustomerType.NORMAL,new StrategyNormal());
strstegyMap.put(CustomerType.DUAMOND,new StrstegyDiamond());
strstegyMap.put(CustomerType.PLATINUM,new StrstegyPlatinum());
}


private StrategyFactory() {
}

public static Strategy getInstance(CustomerType type){
return strstegyMap.get(type);
}
}

5.调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {

Double pay = 100.00D;

Strategy strategyNormal = StrategyFactory.getInstance(CustomerType.NORMAL);
Context contextNormal = new Context(strategyNormal);
System.out.println(CustomerType.NORMAL.toString() + "应付:" + contextNormal.count(pay));

Strategy strategyPlatinum = StrategyFactory.getInstance(CustomerType.PLATINUM);
Context contextPlatinum = new Context(strategyPlatinum);
System.out.println(CustomerType.PLATINUM.toString() + "应付:" +contextPlatinum.count(pay));


Strategy strategyDuamond = StrategyFactory.getInstance(CustomerType.DUAMOND);
Context contextDuamond = new Context(strategyDuamond);
System.out.println(CustomerType.DUAMOND.toString() + "应付:"+contextDuamond.count(pay));

}

结果 :

1
2
3
CustomerType{index=0, message='普通用户'}应付:100.0
CustomerType{index=1, message='白金用户'}应付:98.0
CustomerType{index=2, message='钻石用户'}应付:90.0

这样的话,以后如果还要加别的会员类型,直接在枚举兑现各种增加类型,然后实现策略接口,在策略工厂中稍作修改就可以实现功能,不用去修改业务主体代码,减少了if else 的判断。

四、优化

在策略工厂中,如果新增加了一种策略,那么就需要在静态代码块中new 出一种策略,放进map 中,显然是不符合解耦原则的。我们可以利用spring 中的 InitializingBean 接口中的 afterPropertiesSet 方法,讲策略类交给spring管理,在初始化后,将该对象和类型放入map 中。

调整后,在策略工厂中删除静态代码块,增加一个注册策略的方法。其余策略都实现InitializingBean 接口,重写 afterPropertiesSet 方法,调用策略工厂的注册方法。这样不管有多少策略,只要实现策略基类和InitializingBean 就好了。

改造的策略工厂如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StrategyFactory {
private static Map<CustomerType, Strategy> strstegyMap = new HashMap<CustomerType,Strategy>();
// 将策略保存进map
public static void register(CustomerType type, Strategy strategy){
strstegyMap.put(type,strategy);
}

private StrategyFactory() {
}

public static Strategy getInstance(CustomerType type){
return strstegyMap.get(type);
}
}

策略实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component("StrategyNormal")
public class StrategyNormal implements Strategy, InitializingBean{

@Override
public Double count(Double pay) {
return pay ;
}

@Override
public void afterPropertiesSet() throws Exception {
StrategyFactory.register(CustomerType.NORMAL,this);
}
}

Demo 地址 : https://github.com/huiblog/design_pattern.git

部分参考:https://mp.weixin.qq.com/s/VSjVx5kf-Rc7QifEs4xf6A