所谓策略模式(Strategy Pattern),就是将策略 (算法) 封装为一个对象,易于相互替换,如同 USB 设备一样可即插即用;如果将策略、具体的算法和行为,编码在某个类或客户程序内部,将导至事后的修改和扩展不易。
  当有多种「策略」时,通常的作法就是将这些个策略,和这些策略的算法、行为,分别封装在各个类中,并让这些类,去继承某个公用的抽象类或接口。接着在客户 程序中,就可动态引用,且易于更换这些不同的「策略」,不会因为日后添加、修改了某一个「策略」,就得重新修改、编译多处的源代码。此即为一种「封装变化 点」的做法,将常会变化的部分进行抽象、定义为接口,亦即实现「面向接口编程」的概念。且客户程序 (调用者) 只须知道接口的外部定义即可,具体的实现则无须理会。
   策略模式(Strategy Pattern)在外形上与状态模式很相似,但在意图上有些不同。其意图是使这些算法可以相互替换,并提供一种方法来选择最合适的算法。
   策略模式(Strategy Pattern)的UML图如下:

                        设计模式--Strategy 策略模式_设计模式
在策略模式里主要有三种角色:环境角色、抽象策略角色和具体策略角色
1、环境(Context)角色:持有一个抽象策略(Strategy)角色的引用。也叫上下文。
2、抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或一个抽象类来实现。
3、具体策略(ConcreteStrategy)角色:包装了相应的算法和行为。
下面我们用代码来示例策略模式,程序如下图:

                        设计模式--Strategy 策略模式_设计模式_02
一、策略模式基本思路示例

1、环境(Context)角色:

设计模式--Strategy 策略模式_设计模式_03设计模式--Strategy 策略模式_设计模式_04
using System;
using
 System.Collections.Generic;
using
 System.Linq;
using
 System.Text;

namespace
 MyStrategyPattern
{
    #region 定义Context类

    class Context
    {
        private
 Strategy _strategy;

        #region 构造函数

        public Context(Strategy strategy)
        {
            this._strategy =
 strategy;
        }
        #endregion


        #region 定义算法接口
        //具体的算法由传入的strategy对象的AlgorithmInterface方法来实现
        public void ContextInterface()
        {
            _strategy.AlgorithmInterface();
        }
        #endregion

    }
    
#endregion
}

2、抽象策略(Strategy)角色:

设计模式--Strategy 策略模式_设计模式_03设计模式--Strategy 策略模式_设计模式_04
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyStrategyPattern
{
    #region 抽象策略类Strategy,定义了具体策略类的共有算法接口
    abstract  class Strategy
    {
        public abstract void AlgorithmInterface();
    }
    #endregion
}

3、具体策略(ConcreteStrategy)角色:

设计模式--Strategy 策略模式_设计模式_03设计模式--Strategy 策略模式_设计模式_04
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyStrategyPattern

    //定义了一系列的具体策略类,它们继承自抽象策略类

    class ConcreteStrategyA : Strategy
    {
        public override void AlgorithmInterface()
        {
            Console.WriteLine("使用了算法A来处理Context对象");
        }
    }

    class ConcreteStrategyB : Strategy
    {
        public override void AlgorithmInterface()
        {
            Console.WriteLine("使用了算法B来处理Context对象");
        }
    }

    class ConcreteStrategyC : Strategy
    {
        public override void AlgorithmInterface()
        {
            Console.WriteLine("使用了算法C来处理Context对象");
        }
    }
}

4、客户应用代码

设计模式--Strategy 策略模式_设计模式_03设计模式--Strategy 策略模式_设计模式_04
            #region 基本思路示例
            Console.WriteLine("----策略模式基本思路示例----");
            Context context;
            //调用不同的算法来处理对象,算法的差异在Context传参时(new ConcreteStrategyA())决定
            context = new Context(new ConcreteStrategyA());
            context.ContextInterface();

            context = new Context(new ConcreteStrategyB());
            context.ContextInterface();

            context = new Context(new ConcreteStrategyC());
            context.ContextInterface();


            #endregion

二、求和计算的策略模式示例
1、环境(Context)角色:CalculateContext.

设计模式--Strategy 策略模式_设计模式_03设计模式--Strategy 策略模式_设计模式_04
using System;
using
 System.Collections.Generic;
using
 System.Linq;
using
 System.Text;

namespace
 MyStrategyPattern
{
    class
 CalculateContext
    {
        ICalculateStrategy _strategy;
        public
 CalculateContext(ICalculateStrategy strategy)
        {
            _strategy =
 strategy;
        }

        public void PerformCalculation(List<int>
 list)
        {
            Console.WriteLine(string.Format("对列表中所有整数求和,结果为:{0}"
, _strategy.Sum(list)));
        }
    }
}

2、抽象策略(Strategy)角色:ICalculateStrategy

设计模式--Strategy 策略模式_设计模式_03设计模式--Strategy 策略模式_设计模式_04
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyStrategyPattern
{
    interface  ICalculateStrategy
    {
        int Sum(List<int> list);
    }
}

3、具体策略(ConcreteStrategy)角色:CalculateStrategies

 

设计模式--Strategy 策略模式_设计模式_03设计模式--Strategy 策略模式_设计模式_04
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyStrategyPattern
{
    #region 方法A
    class ConcreteCalculateStrategyA : ICalculateStrategy
    {
        //利用List的Sum功能对List中整数求和
        public int Sum(List<int> list)
        {
            Console.WriteLine("----。使用方法A进行求和----");
            return list.Sum();
        }
    }
    #endregion

    #region 方法B
    class ConcreteCalculateStrategyB : ICalculateStrategy
    {
        //使用传统的遍历方法,对List中整数求和
        public int Sum(List<int> list)
        {
            Console.WriteLine("----。使用方法B进行求和----");
            int result = 0;
            foreach(int value in list)
            {
                result += value;
            }
            return result;
        }
    }
    #endregion


}

4、客户应用代码

设计模式--Strategy 策略模式_设计模式_03设计模式--Strategy 策略模式_设计模式_04
           #region 求和计算的策略模式示例
            Console.WriteLine("\n\n----求和计算的策略模式示例----");
            List<int> lst = new List<int>
();
            lst.Add(3
);
            lst.Add(6
);
            lst.Add(8
);
            lst.Add(9
);

            CalculateContext caltext;
            caltext = new CalculateContext(new
 ConcreteCalculateStrategyA());
            caltext.PerformCalculation(lst);

            caltext = new CalculateContext(new
 ConcreteCalculateStrategyB());
            caltext.PerformCalculation(lst);

            Console.ReadKey();

            #endregion

效果如下图:
                        设计模式--Strategy 策略模式_设计模式_19

总结:

Strategy Pattern 适用的情景:
1、应用中的许多类,在解决某些问题时很相似,但实现的行为有所差异。比如:不同功能的程序,都可能要用到「排序」算法。
2、根据运行环境的不同,需要采用不同的算法。比如:在手机、PC 计算机上,因硬件等级不同,必须采用不同的排序算法。
3、针对给定的目的,存在多种不同的算法,且我们可用代码实现算法选择的标准。
4、需要封装复杂的数据结构。比如:特殊的加密算法,客户程序仅需要知道调用的方式即可。
5、同上,算法中的罗辑和使用的数据,应该与客户程序隔离时。

Strategy Pattern 的优点:
1、简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独做测试。
2、避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
3、高内聚、低偶合。

Strategy Pattern 的缺点:
1、因为每个具体策略都会产生一个新类,所以会增加需要维护的类的数量。
2、选择所用具体实现的职责由客户程序承担,并转给 Context 对象,并没有解除客户端需要选择判断的压力。
3、若要减轻客户端压力,或程序有特殊考量,还可把 Strategy 与 Simple Factory 两种 Pattern 结合,即可将选择具体算法的职责改由 Context 来承担,亦即将具体的算法,和客户程序做出隔离。

前往:设计模式学习笔记清单