策略模式

​ 策略(strategy)模式属于对象的行为模式。其用途是针对一组算法,将每个不同算法封装到具有共同接口的独立类中,从而使他们可以相互替换。即使 算法和对象分开来,使得算法可以独立于使用它的客户而变化。

场景

某个市场人员接到单后的报价策略。报价策略很复杂,可以简单作如下分类:

  • 新客户小批量,不打折
  • 新客户大批量,打9折
  • 老客户小批量,打8.5折
  • 老客户大批量,打8折

通常使用if…else或者switch…case进行表示,代码如下:

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
package designMode.strategy;

/**
* @author YoungDream
* @since 2019/4/18 1:02
*/
public class MyTest {
public double getPrice(CustomerType type, double price) {
if (type == CustomerType.NEW_SMALL) {
System.out.println("不打折");
return price;
} else if (type == CustomerType.NEW_BIG) {
System.out.println("打9折");
return price * 0.9;
} else if (type == CustomerType.OLD_SAMLL) {
System.out.println("打85折");
return price * 0.85;
} else if (type == CustomerType.OLD_BIG) {
System.out.println("打8折");
return price * 0.8;
}
return price;
}
}
enum CustomerType {
NEW_SMALL, NEW_BIG, OLD_SAMLL, OLD_BIG;
}

这样实现起来比较简单,符合一般开人员的思路,但是当类型特别多,算法比较复杂时,整个条件控制代码会变得很长,难于维护。这时我们可以采用策略模式,将不同的策略分开,委派给不同的对象管理。

模式结构

结构图

图1

  • Context环境角色 : 持有一个Strategy的引用。
  • Strategy抽象策略角色: 这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • ConcreteStrategy具体策略角色: 包装了相关的算法或行为。

代码

Strategy抽象策略角色:

1
2
3
4
5
6
7
8
9
10
11
12
13
package designMode.strategy;

/**
* @author YoungDream
* @since 2019/4/18 1:14
*/
public interface Strategy {
/**
* @param price 原价
* @return 打折后的价格
*/
double getPrice(double price);
}

ConcreteStrategy具体策略角色

1
2
3
4
5
6
7
public class NewCustomerSmallStrategy implements Strategy {
@Override
public double getPrice(double price) {
System.out.println("小客户小批量");
return price;
}
}
1
2
3
4
5
6
7
public class NewCustomerBigStrategy implements Strategy {
@Override
public double getPrice(double price) {
System.out.println("小客户大批量");
return price * 0.9;
}
}
1
2
3
4
5
6
7
public class OldCustomerSmallStrategy implements Strategy {
@Override
public double getPrice(double price) {
System.out.println("老客户小批量");
return price * 0.85;
}
}
1
2
3
4
5
6
7
public class OldCustomerBigStrategy implements Strategy {
@Override
public double getPrice(double price) {
System.out.println("老客户大批量");
return price * 0.8;
}
}

Context环境角色

1
2
3
4
5
6
7
8
9
10
11
public class Context {
private Strategy strategy;

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

public void printPrice(double price) {
System.out.println("价格为: " + strategy.getPrice(price) + "元");
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
package designMode.strategy;

/**
* @author YoungDream
* @since 2019/4/18 1:25
*/
public class Client {
public static void main(String[] args) {
Strategy strategy = new NewCustomerBigStrategy();
Context context = new Context(strategy);
context.printPrice(100);
}
}

控制台

1
2
小客户大批量
价格为: 90.0元

从上面的示例可以看出,策略模式仅仅封装算法,提供新的算法插入到已有系统中,以及老算法从系统中“退休”的方法,策略模式并不决定在何时使用何种算法。在什么情况下使用什么算法是由客户端决定的。

总结

优点

  • 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
  • 使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。

缺点

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
  • 由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。

行为模式