软件设计模式修炼 -- 策略模式

2年前 (2022) 程序员胖胖胖虎阿
272 0 0

策略模式用于算法的自由切换和扩展,实现了算法定义和算法分离的使用

模式动机

要完成一项任务,可以有多种不同的方式,例如人们外出旅游时可以选择多种不同的出行方式,如自行车、坐汽车、坐高铁或乘飞机等,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该任务。

在实际的软件开发中,一项功能也有很多算法可以实现,如果我们直接把多种算法集中在一个类,或者说使用条件判断语句来进行选择,无疑会增加代码复杂性,不利于维护。

为了解决这些问题,可以定义一些独立的类来封装不同的算法,每一类封装一个具体的算法,将每一个封装算法的类称之为策略(Strategy)。为了保证策略的一致性,一般会用一个抽象的策略类来做算法的定义,每种具体算法对应于一个具体策略类。

模式定义

策略模式(Strategy Pattern)定义一系列算法,将每个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称政策模式(Policy)。策略模式是一种对象行为模式。

Define a famliy of algorithms, encapsulate each one,and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

模式分析

说到策略模式就不得不提一下状态模式了,一个是对状态的封装,另一个则是对算法的封装,因此实现方式上两者有许多共同点,理解起来并不复杂

点这里了解状态模式

策略模式是对算法的封装,把算法的责任和算法本身分割开来,委托给不同对象管理。策略模式通常把一系列算法封装到一系列的策略类里面,作为一个抽象策略类的子类。

由此可以得出结构类图如下:

软件设计模式修炼 -- 策略模式

环境类(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();
    }
}

对于用户而言,使用环境类时只需注入一个具体策略对象即可。用户可以灵活更换具体策略类,比如使用配置文件,增加新的具体策略类也很方便,因此策略模式相当于“即插即用的算法”。

需要注意的是,策略模式并不负责“哪一个具体策略类适用于哪一种情况”这个决定,换言之,应当由客户决定在什么情况下使用什么具体策略角色,一定程度上提高了系统灵活性,但前提是客户需要理解所有具体策略类之间的区别及最佳使用场景。

模式优缺点

策略模式的优点:

  • 策略模式提供了对开闭原则的完美支持,将算法的使用与实现完美分离,用户可以在不修改原有系统的基础上选择和新增算法
  • 策略模式的等级结构定义了一个算法族,恰当使用继承可以把公共的代码移到父类里面,避免重复代码
  • 可以避免使用多重条件转移语句

策略模式的缺点:

  • 客户端必须知道所有的策略类,并自行决定使用哪一种算法
  • 策略模式将产生很多策略类和对象,可以通过享元模式在一定程度上减少对象的数量

策略模式与状态模式的比较

由于结构的相似性,所以很容易会把策略模式和状态模式混肴,但它们是为解决不同的问题而设计的,是完全不同的两种模式。如何区分是策略模式还是状态模式?其区别如下:

  • 如果环境角色存在多种状态,而且这些状态之间可以进行转换,则应使用状态模式,在状态模式在,环境类在生命周期中,会有一个不同的状态对象被创建和使用;如果环境角色只有一个状态,那么应当使用策略模式,因为一旦环境角色选择了一个具体策略类,那么在整个环境类的生命周期里它都不会改变这个具体策略类
  • 策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类;状态模式的环境类与状态类之间存在一种双向关系,以便实现状态的切换
  • 使用策略模式,客户端需要知道所选的具体策略模式是哪一个,而使用状态模式,客户端无须关心具体状态,环境类的状态会根据用户的操作自动转换
  • 如果系统中某个类的对象存在多种状态,不同状态下行为有差异,而且这些状态之间可以发送转换时使用状态模式;如果某个类的行为存在多种实现方式,而且这些方式可以互换时使用策略模式

版权声明:程序员胖胖胖虎阿 发表于 2022年10月5日 上午6:48。
转载请注明:软件设计模式修炼 -- 策略模式 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...