ID #33869

C# Design Patterns (2) - Strategy

  Strategy Pattern (策略模式)

  所谓 Strategy Pattern 的精神,就是将策略 (算法) 封装为一个对象,易于相互替换,如同 USB 设备一样可即插即用;而不是将策略、具体的算法和行为,硬编码在某个类或客户程序中,导至事后的修改和扩展不易。

  若有多种「策略」,就将这些个策略,和这些策略的算法、行为,封装在各个类中,并让这些类,去继承某个公用的抽象类或接口。接着在客户程序中,就可动态引用,且易于更换这些不同的「策略」,不会因为日后添加、修改了某一个「策略」,就得重新修改、编译多处的源代码。此即为一种「封装变化点」的做法,将常会变化的部分进行抽象、定义为接口,亦即实现「面向接口编程」的概念。且客户程序 (调用者) 只须知道接口的外部定义即可,具体的实现则无须理会。

  The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

   - Design Patterns: Elements of Reusable Object-Oriented Software

  Strategy Pattern 适用的情景:

  应用中的许多类,在解决某些问题时很相似,但实现的行为有所差异。比如:不同功能的程序,都可能要用到「排序」算法。

  根据运行环境的不同,需要采用不同的算法。比如:在手机、PC 计算机上,因硬件等级不同,必须采用不同的排序算法。

  针对给定的目的,存在多种不同的算法,且我们可用代码实现算法选择的标准。

  需要封装复杂的数据结构。比如:特殊的加密算法,客户程序仅需要知道调用的方式即可。

  同上,算法中的罗辑和使用的数据,应该与客户程序隔离时。

貧匯匈 1 2 3 4 和匯匈

C# Design Patterns (2) - Strategy

  夕 1 宸嫖葎載謹慕汐才猟亀脅奚竃・狛議 Strategy 将灸 Class Diagram

01_Shell.ASPx.cs
using System;
using com.cnblogs.WizardWu.sample01;

//人薩殻會
public partial class _01_Shell : System.Web.UI.Page
{    
    protected void Page_Load(object sender, EventArgs e)
    {
        //峇佩斤・
        Context context;

        context = new Context(new ConcreteStrategyA());
        Response.Write(context.ContextInterface() + "<br>");

        context = new Context(new ConcreteStrategyB());
        Response.Write(context.ContextInterface() + "<br>");

        context = new Context(new ConcreteStrategyC());
        Response.Write(context.ContextInterface() + "<br>");
    }
}

namespace com.cnblogs.WizardWu.sample01
{
    //渇・麻隈窃 (呀辛喘俊笥)。協吶阻侭嗤貨待議巷慌俊笥
    abstract class Strategy
    {
        //麻隈俶勣頼撹議孔嬬
        public abstract string AlgorithmInterface();
    }

    //醤悶麻隈窃A
    class ConcreteStrategyA : Strategy
    {
        //麻隈A糞・圭隈
        public override string AlgorithmInterface()
        {
            return "麻隈A糞・";
        }
    }

    //醤悶麻隈窃B
    class ConcreteStrategyB : Strategy
    {
        //麻隈B糞・圭隈
        public override string AlgorithmInterface()
        {
            return "麻隈B糞・";
        }
    }

    //醤悶麻隈窃C
    class ConcreteStrategyC : Strategy
    {
        //麻隈C糞・圭隈
        public override string AlgorithmInterface()
        {
            return "麻隈C糞・";
        }
    }

    //峇佩斤・。俶勣寡喘辛紋算貨待峇佩議斤・
    class Context
    {
        Strategy strategy;

        public Context(Strategy strategy)        //更夛痕方
        {
            this.strategy = strategy;
        }

        //峇佩斤・卆正噐貨待斤・議荷恬圭隈
        public string ContextInterface()
        {
            return strategy.AlgorithmInterface();
        }
    }

} // end of namespace


/* 

貧匯鐙・ C#蝕窟WPF/Silverlight強鮫式嗄老狼双縮殻(Game Course)・(励)糞・2D繁麗強鮫・
和匯鐙・ C#蝕窟WPF/Silverlight強鮫式嗄老狼双縮殻(Game Course)・(鎗)頼胆卞強

貧匯匈 1 2 3 4 和匯匈

  结行结果:

  算法A实现

  算法B实现

  算法C实现

  */

  上方的「Shell (壳)」示例中,最下方的 Context 类,为一种维护上下文信息的类,让 Strategy 类 (或 IStrategy 接口) 及其子类对象的算法,能运行在这个上下文里。

  下方的图 2 及其代码,为此 Shell 示例和 Strategy Pattern 的一个具体实现示例。我们知道,Linux 和 Windows 操作系统,在文本文件的「换行符」是不同的,前者为「n」,后者为「rn」。若我们要设计一个文本编辑工具,或简易的编程工具,必须要能随时转换这两种不同操作系统的换行符 (假设 .net 已可执行于 Linux 上)。此时我们即不该在客户程序 (如:ASP.NET 页面的 Code-Behind) 中用硬编码 switch...case 的 hard coding 寫法,而应如下方示例,以 Strategy Pattern 实现此一功能,并将这些算法 (策略) 各自封装在各个子类中 (如 ASP.NET 项目的 App_Code 文件夹中的类,或其他类库项目中的类),使他们易于组合、更换,便于日后的维护和修改。

C# Design Patterns (2) - Strategy

C# Design Patterns (2) - Strategy

  图 3 示例 02_Strategy.ASPx.cs 的执行结果

  若未用任何 Pattern 的客户程序,可能就如下方的硬编码,将「换行符」和算法,直接写死在 ASP.NET 的 Code-Behind 里,导至事后的维护和扩展不易。 

hard coding
protected void DropDownList1_SelectedIndExchanged(object sender, EventArgs e)
{
    switch(DropDownList1.SelectedValue)
    {
        case "Linux":
            Label1.Text = strWindowsText.Replace("rn", "n");
            break;
        case "Windows":
            Label1.Text = strLinuxText.Replace("n", "rn");
            break;
        default:
            Label1.Text = String.Empty;
            break;
    }
}

  此外,若用 Simple Factory Pattern (简单工厂模式) 虽然也能解决上述硬编码的问题,但就如我们前一篇帖子「C# Design Patterns (1) - Factory Method」曾经提过的缺点,日后若要添加或修改功能时,仍要修改、重新编译 server-side 的「工厂类」。所以在此种情况下,用 Strategy 会是比 Simple Factory 更好的选择。

  --------------------------------------------------------

  Strategy Pattern 的优点:

  简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独做测试。

  避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。

  高内聚、低偶合。

  Strategy Pattern 的缺点:

  因为每个具体策略都会产生一个新类,所以会增加需要维护的类的数量。

  选择所用具体实现的职责由客户程序承担,并转给 Context 对象,并没有解除客户端需要选择判断的压力。

  若要减轻客户端压力,或程序有特殊考量,还可把 Strategy 与 Simple Factory 两种 Pattern 结合,即可将选择具体算法的职责改由 Context 来承担,亦即将具体的算法,和客户程序做出隔离。有关这方面的概念和示例,可参考伍迷的「大话设计模式」一书 [10]。

  --------------------------------------------------------

  此外,从行为上来看,State Pattern 和 Strategy Pattern 有点类似,但前者可看作后者的动态版本。

  State:看当前是什么状态,就采取什么动作。

  Strategy:看需求及情景为何,采用适当的策略。

  State 中,当对象内部的状态改变时,它可切换到一组不同的操作,从而改变对象的行为,例如 GoF 示例中的 TCP 连接;而 Strategy 是直接采用适当的策略 (算法),如本帖示例中,不同的操作系统,实现换行的具体算法类 LinuxStrategy 与 WindowsStrategy。


2009-06-22 00:00
阅读:
I'm VC , Just U know Y
本站部分文章来源于互联网,版权归原作者所有。

延伸阅读:

如何用C#编写文本编辑器

C# Design Patterns (3) - Decorator

C# Design Patterns (4) - Proxy

我的Design Pattern之旅[4]:使用Generic改進Strategy P

设计模式(C#) - 策略模式(Strategy Pattern)