设计模式六之策略模式

通过理论,代码示例,Android源码来学习策略模式

介绍

在软件开发中常常遇见这样的问题:实现某个功能可以有多种算法或者策略,我们根据实际情况选择不同的算法或者策略来完成该功能。例如,排序算法,可以有多种的实现方式。

那么,针对这种情况,应该怎么处理?是将这些算法写在一个类中,每一个方法对应一个具体的排序算法;还是将这些排序算法封装在同一个方法中,通过 if…else 或者 case 等条件判断语句来选择具体的算法。这 2 种实现方法我们都可以称之为硬编码。当然,这样是可以实现需求,但是,当很多算法在一个类时,这个类就会变得很臃肿,维护成本就会变高,并且在维护时容易发生错误,如果我们需要新增或者修改算法类的源码,这个就需要动封装好的类,那么这就违背了单一职责和开闭原则了。

如果将这些算法或者策略抽象出来,提供统一的接口,不同的算法或者策略有不同的实现类,这样在程序客户端就可以通过注入不同的实现对象来实现算法或者策略的动态替换,这种模式的可扩展性,可维护性也就更高,这就是我们今天要学习的策略模式。

定义

策略模式定义了一系列算法,并将每一个算法封装起来,而且使他们还可以相互替换,策略模式让算法独立于使用它的客户而独立变化。

使用场景

  • 针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
  • 需要安全的封装多种同一类型操作时。
  • 出现同一抽象类有多个子类,而又需要使用 if-else 或者 switch-case 来选择具体子类时。

UML 类图

  • Context :用来操作策略的上下文环境
  • Stragety : 策略的抽象
  • ConcreteStragetyA, ConcreteStragetyB 具体策略实现。

简单示例

需求:对交通工具计算车费,一般轿车,中等轿车,豪华轿车。

1
2
3
4
5
6
7
8
9
10
11
12
13
 /*
* 计算费用接口
*
*/
public interface ICalculateStrategy {

/**
* 按距离计算车费
* @param km 公里数
* @return
*/
int calculatePrice(int km);
}

各种车的计算方式

1
2
3
4
5
6
7
8
9
10
11
12
13
//普通车
public class GeneralCar implements ICalculateStrategy {
@Override
public int calculatePrice(int km) {
if (km > 0 && km <= 5)
return 5;
if (km > 5 && km <= 7)
return 7;
if (km > 7 && km <= 10)
return 10;
return 10;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
//中级车
public class MediumCar implements ICalculateStrategy {
@Override
public int calculatePrice(int km) {
if (km > 0 && km <= 5)
return 6;
if (km > 5 && km <= 7)
return 9;
if (km > 7 && km <= 10)
return 12;
return 12;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
//高级车
public class LuxuryCar implements ICalculateStrategy {
@Override
public int calculatePrice(int km) {
if (km > 0 && km <= 5)
return 8;
if (km > 5 && km <= 7)
return 13;
if (km > 7 && km <= 10)
return 15;
return 13;
}
}

我们在创建一个操作 Context 策略的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TransportationCalculator {

/**
* 交通工具计算费用策略类
*/
private ICalculateStrategy iCalculateStrategy = new GeneralCar();


/**
* 设置策略
* @param calculateStrategy
*/
public void setStrategy(ICalculateStrategy calculateStrategy) {
this. iCalculateStrategy = calculateStrategy;
}


public int calcu(int km){
return iCalculateStrategy.calculatePrice(km);
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void test8(){
TransportationCalculator transportationCalculator = new TransportationCalculator();
System.out.println("普通车 1 km RMB:" + transportationCalculator.calcu(5));


transportationCalculator.setStrategy(new MediumCar());
System.out.println("中级车 1 km RMB:" + transportationCalculator.calcu(5));


transportationCalculator.setStrategy(new LuxuryCar());
System.out.println("豪华车 1 km RMB:" + transportationCalculator.calcu(5));
}

Output:

1
2
3
普通车 1 km RMB:5
中级车 1 km RMB:6
豪华车 1 km RMB:8

有没有发现这种写法不仅结构变得清晰,而且还易维护,扩展性也很强。

总结

策略模式主要用来分离算法,在相同的行为抽象下有不同的具体实现策略。在这个模式很好地演示了开闭原则,也就是定义接口抽象,注入不同的实现,从而达到很好的可扩展性。

优点:

  • 结构清晰明了、使用简单直观;
  • 耦合度相对而言较低,扩展方便;
  • 操作封装也更为彻底,数据更为安全;

缺点:

  • 随着策略的增加,子类也会变得繁多
坚持原创技术分享,您的支持将鼓励我继续创作!