策略模式
概述
要完成一项任务,可以有多种不同的方式,外出旅游时可以选择多种不同的出行方式,如自行车、坐汽车、坐高铁或乘飞机等,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成任务
在软件开发中,一项功能也有很多算法可以实现,如果我们直接把多种算法集中在一个类,或者说使用条件判断语句来进行选择,则会增加代码复杂性,不利于维护
为了解决这些问题,可以定义一些独立的类来封装不同的算法,每一类封装一个具体的算法,将每一个封装算法的类称之为策略(Strategy)。为了保证策略的一致性,一般会用一个抽象的策略类来做算法的定义,每种具体算法对应于一个具体策略类
模式分析
说到策略模式就不得不提一下状态模式了,一个是对状态的封装,另一个则是对算法的封装,因此实现方式上两者有许多共同点。策略模式是对算法的封装,把对象和算法分割开来
由此可以得出结构类图如下:
环境类(Context)是需要使用算法的对象,它在解决某个问题时可以采用多种策略,在环境类中维护一个对抽象策略类的引用实例,用于定义所采用的策略。假如我们不使用策略模式,可能会存在如下代码:
public class Context {
...
public void algorithm(String type) {
...
if(type == "strategyA") {
// 算法A
} else if(type == "strategyB") {
// 算法B
} else if(type == "strategyC") {
// 算法C
}
...
}
...
}
客户端要调用 Context 类的 algorithm() 方法时,需要根据所传入的参数来选择具体算法,这将导致代码不利于维护,如果需要修改或增加算法,则要修改代码
通过引入一个抽象策略类(Strategy),在其中定义抽象算法,每一个继承它的具体策略类(ConcreteStrategy)使用具体算法实现某个业务逻辑。出现新的算法时,只需要增加一个新的实现了抽象策略类的具体策略类即可
public abstract class AbstractStrategy {
public abstract void algorithm();
}
将每一种具体算法作为该抽象策略类的子类
public class ConcreteStrategyA extends AbstractStrategy {
public void algorithm() {
// 算法A
}
}
对于环境类,在它与抽象策略类之间建立一个关联关系
public class Context {
private AbstractStrategy strategy;
public void setStrategy(AbstractStrategy strategy) {
this.strategy = strategy;
}
public void algorithm() {
strategy.algorithm();
}
}
对于用户而言,使用环境类时只需注入一个具体策略对象即可。用户可以灵活更换具体策略类,比如使用配置文件,增加新的具体策略类也很方便,因此策略模式相当于“即插即用的算法”
需要注意的是,策略模式并不负责“哪一个具体策略类适用于哪一种情况”这个决定,换言之,应当由客户决定在什么情况下使用什么具体策略角色。如果环境对象存在多种状态,而且这些状态之间可以进行转换,则应使用状态模式。如果环境对象只有一个状态,那么应当使用策略模式