1.定义:

定义一组算法,将每个算法封装起来,并使它们可以互相替换。

策略模式让算法的变化独立于使用它的客户端。

是一种 行为型设计模式:涉及到对象之间如何通信和交互

2.一个普通的例子

想从A点到达B点,有两种方案:

  • 方案一:低速路,全程限速60

  • 方案二:高速路,全程限速100,但是需要收费

现在我们在高德地图中,可以让用户选是哪种路线,然后进行导航

用户选择低速路,我们在高德地图中有对应的低速导航算法;如果用户选择高速路,在高德地图中有对应的高速导航算法

我们会根据配置,在运行时调用特定的计算逻辑。

策略设计模式的本质:如何设计软件,以便你可以轻松替换底层算法,而不会影响周围的代码。无论我们使用最快路线,最短路线,避开收费路段,还是其他100种不同的算法,周围的代码都可以保持不变。我们可以透明的替换底层算法。

再次回顾 策略模式 的定义
  • 定义一组算法:用于计算或执行某些操作的不同算法

  • 将每个算法封装起来:可以将其想象为一个类,一个方法,一个组件,或者一个子系统,你将它封装在某种可互换的结构中。

  • 并使它们可以互相替换: 可互换,本质上是指让它们暴露相同的接口。如果它们暴露相同的接口,客户端就可以使用该接口。而在幕后,我们可以替换底层的组件。

策略模式赋予我们的能力是:让算法可以独立且透明地变化,而不影响使用它的客户端。客户端完全使用相同的代码,它使用的是某个接口,但是在幕后,我们替换了底层的算法。

策略模式的另一种解释:
  • 将可互换的逻辑置于接口之后

3.策略模式的类图

  • 有一个名为策略strategy的接口,策略指的就是算法,这个接口就是为该算法定义的

    • 我们在接口中,放一个名为execute()的方法,表示一种算法

  • 有几个具体的策略,它们是strategy接口的具体实现,是具体的算法本身,它们可以互相替换

  • 有一个上下文Context,上下文内部私有地持有一个策略,并提供两个方法,

    • 一个方法是SetStrategy(),它接收一个策略

    • 另一个方法是 ExecuteStrategy(),它执行底层的策略

3.1高德地图的具体实例:

  • 接口Strategy:在高德地图中,它是一个GetRoute()方法,用于获取具体的道路算法

  • 具体策略:实现了接口Strategy的具体算法

    • ConcreteStrategyA:避开收费路线算法

    • ConcreteStrategyB:获取最快路线

    • ConcreteStrategyC:获取最短路线

    • 一组算法家族,它们都被隐藏在统一的接口之后

  • 上下文Context:执行策略的组件

    • strategy:策略接口,在运行时它会是具体策略算法中的某一个

    • ExecuteStrategy()方法:Navigate()方法,用于导航,无论底层组件是哪一个,这个方法都会保持完全不变

    • SetStrategy()方法:可以设置具体的算法

4.代码示例():
public interface IStrategy
{
    void Execute();
}
​
public class ConcreteStrategyA:IStrategy
{
    public void Execute()
    {
        Console.WriteLine("执行了策略A");
    }
}
​
public class ConcreteStrategyB:IStrategy
{
    public void Execute()
    {
        Console.WriteLine("执行了策略B");
    }
}
​
public class ConcreteStrategyC:IStrategy
{
    public void Execute()
    {
        Console.WriteLine("执行了策略C");
    }
}
​
public class Context
{
    private IStrategy? _strategy;
    
    public void SetStrategy(IStrategy strategy)
    {
        _strategy = strategy;
    }
    
    public void ExecuteStragegy()
    {
        _strategy?.Execute();//仅在_strategy不为null时,才执行
    }
}
​
//调用实例:
Context context = new();
​
context.SetStrategy(new ConcreteStrategyA());//注入策略A
context.ExecuteStrategy();
​
context.SetStrategy(new ConcreteStrategyC());//注入策略C
context.ExecuteStrategy();
//输出结果
执行了策略A
执行了策略C
//从客户端的角度看,它只是调用了ExecuteStrategy(),但我们在运行的时候,替换了底层的具体策略。
5.支付处理的实例:

  • 购物车包含一个Checkout方法用于付款,我们在Checkout中计算金额,然后会使用支付策略

  • 在运行时,支付策略可以是,信用卡支付,PayPal支付,银行转账具体策略中的某一种。

  • 根据我们持有的—IPaymentStrategy接口,我们可以使用某种具体的支付策略完成支付。

  • 每种策略可以完全不同,可以拥有不同的参数,内部行为也可以不同,所有内容都隐藏在完全相同的接口背后

6.策略模式的优缺点:
  • 优点:

    • 可扩展性:一旦我们定义了策略接口,并且客户端通过该接口工作,我们就可以非常轻松地扩展具体策略的数量,想创建多少就创建多少。上下文并不关心它使用的是哪一个具体策略,只要这个策略实现了接口即可。

    • 灵活性:我们可以轻松替换底层的具体策略,而无需修改客户端的任何代码。这种替换不仅可以在加载配置时发生,也可以在运行时动态变化。

    • 关注点分离:每个策略都封装了与其相关的特定逻辑,特定算法,每种算法都只负责自己特定的部分。

  • 缺点:

    • 导致类的数量增加:简单的算法分支完全可以使用ifelse或者switch分支,而不必创建大量类,策略算法越复杂,使用策略设计模式的效益越高

    • 如果处理的是一个简单的用例,并且底层算法并不复杂,那么使用这个策略模式,反而会增加复杂度。

    • 不同算法之间共享数据变得复杂。

更多推荐