一、模式动机
为什么要使用策略模式呢?在日常开发中,我们可以发现一种需求可以有不同的方法来实现,比如我们要对一个数组进行排序,就可以使用多种不同的排序方法(选择排序、冒泡排序、快速排序等),每一种排序方法都可以被称作一种策略,我们可以在不同的情况下来选择不同的策略进行排序。在实现的时候我们可能会写一个算法类,包含了所有的排序算法,接着在我们需要排序的类(简称客户端)中创建算法类的对象,然后写一大段if...else来选择对象类中不同的排序算法。这种实现方式比较容易想到,但如果我们要增加一种新的排序算法,那么需要修改两个类:算法类和客户端,扩展和维护起来非常麻烦。为了解决这个问题,我们可以将每一个排序算法封装在一个独立的类中,这样一个独立的类我们称之为一种策略,为了保证这些策略的一致性,我们要建立一个抽象策略类,让包含排序算法的具体策略类实现抽象策略类。
二、模式定义
策略模式定义了算法族,并将这些算法封装起来成为类,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
三、模式结构
策略模式涉及到3个角色:
1、Context:环境类(上下文类),持有一个Strategy的引用,负责调用相关算法;
2、Strategy:抽象策略类,通常由接口或抽象类实现;
3、ConcreteStrategy:具体策略类,实现了抽象策略类。
这3种角色的代码可以用以下方式实现:
抽象策略类Strategy:
public interface Strategy{ /** * 策略中包含的算法 */ void algorithm(); }
具体策略类ConcreteStrategy:
具体策略类实现了抽象策略类:
public class ConcreteStrategyA implements Strategy{ @Override public void algorithm(){ //具体算法或行为 } }
public class ConcreteStrategyB implements Strategy{ @Override public void algorithm(){ //具体算法或行为 } }
环境类Context:
public class Context{ //持有一个Strategy对象 private Strategy strategy; //在构造函数中传入Strategy对象 public Context(Strategy strategy){ this.strategy = strategy; } //调用策略类中的算法 public void contextInterface(){ //if...else可以转移到这里 strategy.algorithm(); } }
可以在客户端Client类中进行测试:
public class Client{ public static void main(String[] args){ ConcreteStrategyA csa = new ConcreteStrategyA(); Context context = new Context(csa); context.contextInterface(); } }
可以看到,在环境类Context中持有一个抽象的Strategy对象,而不是某一个具体的策略类,这样利用多态特性,当有新的排序算法时,我们只需要实现Strategy接口,而不用去修改客户端,也就是针对接口编程,而不是针对实现编程。通过策略模式,算法的使用和算法的实现被分离开来。
四、优缺点
优点:
1、策略类易于扩展和维护;
2、使用策略模式可以避免使用多重条件转移语句
缺点:
1、客户端必须实现知道所有的策略类,并自行决定使用哪一个策略类;
2、在策略模式中将每一种算法都封装成一个策略类,这样会造成策略类过多,维护起来会带来额外开销。
五、适用场景
1、系统中的类主要逻辑相同,只是部分逻辑的实现方法不同,这样使用策略模式就可以动态的选择不同的行为;
2、需要安全地封装同一类型的操作。