👉文章示例代码👈

定义

定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

三个角色

策略模式中主要有三个角色:

  • 抽象策略类Strategy:定义所有支持的算法的公共接口,可以是接口也可以是抽象类。
  • 具体策略类Concrete Strategy:封装具体的算法或者行为。
  • 环境类Context:维护一个策略类的引用,提供给客户端调用。

场景示例

笔者这里针对某超市的会员体系为例。不同等级的会员拥有不同的打折力度,具体情况如下表。

超市会员打折对照表
会员等级 打折力度
普通会员 不打折
黄金会员 九五折
铂金会员 八八折
钻石会员 八折

创建抽象策略

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author zhh
* @description 会员策略
* @date 2020-02-28 16:56
*/
public interface MemberStrategy {

/**
* 打折
*/
void discount();
}

创建具体策略

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* @author zhh
* @description 普通会员策略
* @date 2020-02-28 16:58
*/
public class OrdinaryMemberStrategy implements MemberStrategy {

public void discount() {
System.out.println("普通会员不打折");
}
}


/**
* @author zhh
* @description 黄金会员策略
* @date 2020-02-28 16:59
*/
public class GoldMemberStrategy implements MemberStrategy {

public void discount() {
System.out.println("黄金会员所有商品打九五折");
}
}


/**
* @author zhh
* @description 铂金会员策略
* @date 2020-02-28 17:00
*/
public class PlatinumMemberStrategy implements MemberStrategy {

public void discount() {
System.out.println("铂金会员所有商品打八八折");
}
}


/**
* @author zhh
* @description 钻石会员策略
* @date 2020-02-28 17:01
*/
public class DiamondMemberStrategy implements MemberStrategy {

public void discount() {
System.out.println("钻石会员所有商品打八折");
}
}

创建环境类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author zhh
* @description 打折活动
* @date 2020-02-28 17:03
*/
public class DiscountActivity {

private MemberStrategy memberStrategy;

public DiscountActivity(MemberStrategy memberStrategy) {
this.memberStrategy = memberStrategy;
}

/**
* 打折
*/
public void discount() {
memberStrategy.discount();
}
}

测试类及输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @author zhh
* @description 测试类
* @date 2020-02-28 17:05
*/
public class Test {

public static void main(String[] args) {
String member = "黄金会员";
DiscountActivity discountActivity;

if ("黄金会员".equals(member)) {
discountActivity = new DiscountActivity(new GoldMemberStrategy());
} else if ("铂金会员".equals(member)) {
discountActivity = new DiscountActivity(new PlatinumMemberStrategy());
} else if ("钻石会员".equals(member)) {
discountActivity = new DiscountActivity(new DiamondMemberStrategy());
} else {
discountActivity = new DiscountActivity(new OrdinaryMemberStrategy());
}

discountActivity.discount();
}
}

测试类的输出结果如下:

黄金会员所有商品打九五折

类结构图

以上示例类的结构图如下所示
image.png

问题所在

上述示例代码的测试类即我们的客户端,我们可以看到,我们并未完全消除 if...else 这种条件语句,而且每次方法执行到此处时,都需要创建一个策略对象和一个活动对象,会存在重复创建的现象。

方案改进

为了避免上述的问题,我们结合策略模式和工厂模式在原有的示例代码上进行改造。

新增工厂类

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* @author zhh
* @description 会员策略工厂
* @date 2020-02-28 17:24
*/
public class MemberStrategyFactory {

private static Map<String, MemberStrategy> MEMBER_STRATEGY_MAP = new HashMap<String, MemberStrategy>();

static {
init();
}

private MemberStrategyFactory() {}

/**
* 获取会员策略
* @param member 会员等级
*/
public static MemberStrategy getMemberStrategy(String member) {
MemberStrategy memberStrategy = MEMBER_STRATEGY_MAP.get(member);
return memberStrategy == null ? MEMBER_STRATEGY_MAP.get(MemberKey.ORDINARY) : memberStrategy;
}

/**
* 初始化操作
*/
private static void init() {
MEMBER_STRATEGY_MAP.put(MemberKey.ORDINARY, new OrdinaryMemberStrategy());
MEMBER_STRATEGY_MAP.put(MemberKey.GOLD, new GoldMemberStrategy());
MEMBER_STRATEGY_MAP.put(MemberKey.PLATINUM, new PlatinumMemberStrategy());
MEMBER_STRATEGY_MAP.put(MemberKey.DIAMOND, new DiamondMemberStrategy());
}

/**
* 会员键值
*/
private interface MemberKey {
String ORDINARY = "普通会员";
String GOLD = "黄金会员";
String PLATINUM = "铂金会员";
String DIAMOND = "钻石会员";
}
}

调整测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @author zhh
* @description 测试类
* @date 2020-02-28 17:05
*/
public class Test {

public static void main(String[] args) {
String member = "黄金会员";
DiscountActivity discountActivity = new DiscountActivity(MemberStrategyFactory.getMemberStrategy(member));
discountActivity.discount();
}
}

测试类的输出结果如下:

黄金会员所有商品打九五折

这里我们可以看到,通过策略模式和工厂模式的组合使用,我们消除了客户端的 if...else 条件转义语句,使客户端变得相对简单。

总结

策略模式的使用方式一般并不是独立使用的,有可能需要结合工厂模式、单例模式、享元模式等设计模式来一起实现。

适用场景

  • 多个类的区别仅仅在于表现行为的不同。
  • 一个系统需要动态地在几种算法中选择一种。

优点

  • 可以避免使用多重条件语句。
  • 符合开闭原则,可以在不修改原有代码的情况下,灵活增加新算法。
  • 各算法彼此独立,且对客户端隐藏具体的算法实现,提高了算法的保密性和安全性。

缺点

  • 客户端必须理解所有策略算法的区别,并且自行决定使用哪一个策略类。
  • 会造成很多的策略类。

参考