首页 > 代码库 > [设计模式-04]策略模式-对开发和测试的意义

[设计模式-04]策略模式-对开发和测试的意义

       面向对象和面向过程最大的区别在于复用性和扩展性。这里的复用性不仅仅指代码级别的复用性,更重要的是模块级别的复用性。良好的面向对象设计面向需求的变更只需要变更模块运行的流程即可,更复杂的需求变更也只需要增加具体的执行模块即可。if...else在代码级别确实做到了一些复用性,因为避免了代码拷贝的问题,但是在面对需要变更执行流程或者增加执行模块时却需要修改代码,违背了五大原则中的“面向修改封闭,面向扩张开放”的原则。

       如果是TDD开发,对于if...else,写单元测试的时候,有多少个if...else就需要写多少个UT,并且确保if里面的执行了,而else里面的都没有执行。这样也大大增加了UT开发的工作量,如果采用了策 略模式,只需要针对策略模式的管理者写一个UT即可,大大减少了UT的开发工作量。同时代码的复用性和扩展性大大提高,面对复杂的功能模块,策略模式依赖接口,使用Moq能够很好地模拟具体的情形。这也是if...else所做不到的。

       1.策略模式的使用情形

       多个相关的类仅行为有异。这些类是几种情形下的具体处理形式,比如红灯停,绿灯行,黄灯等,根据会员级别给予不同程度的折扣等等。

       需要使用一个算法的不同变体。比如针对一个数据集合,针对某些列进行排序,统计分析等,比如针对性别,年龄,专业进行统计。

       算法使用客户不应该知道的数据。使用策略模式之后,客户端值接触到Manager类,只需要传入一个具体操作类的引用即可。隐藏了实现细节,确保了代码安全。

       一个类定义了多种相似行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

       2.策略模式结构

      

        环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
        抽象策略类(Strategy):定义所有支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy定义的算法。
        具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。

        AlgorithmContext类

namespace StrategyPatternDemo
{
    public class AlgorithmContext
    {
        private IAlgorithmInterface algorithmInterface;

        public AlgorithmContext(IAlgorithmInterface algorithmInterface)
        {
            this.algorithmInterface = algorithmInterface;
        }

        public string DoStrategyAlgorithm()
        {
            return algorithmInterface.DoAlgorithm();
        }
    }
}

ConcreteStrategyA类

namespace StrategyPatternDemo
{
    class ConcreteStrategyA:IAlgorithmInterface
    {
        public string DoAlgorithm()
        {
            return "StrategyA";
        }
    }
}
ConcreteStrategyB类

namespace StrategyPatternDemo
{
    class ConcreteStrategyB:IAlgorithmInterface
    {
        public string DoAlgorithm()
        {
            return "StrategyB";
        }
    }
}
ConcreteStrategyC类

namespace StrategyPatternDemo
{
    class ConcreteStrategyC:IAlgorithmInterface
    {
        public string DoAlgorithm()
        {
            return "StrategyC";
        }
    }
}
IAlgorithmInterface接口

namespace StrategyPatternDemo
{
    public interface IAlgorithmInterface
    {
        string DoAlgorithm();
    }
}
单元测试

namespace StrategyPatternTestProject
{
    /// <summary>
    ///这是 AlgorithmContextTest 的测试类,旨在
    ///包含所有 AlgorithmContextTest 单元测试
    ///</summary>
    [TestClass]
    public class AlgorithmContextTest
    {
        private AlgorithmContext algorithmContext;
        private IAlgorithmInterface algorithmInterface;
        private string except;
        private string actual;

        [TestInitialize]
        public void Initalize()
        {
            algorithmInterface = new ConcreteStrategyA();
            algorithmContext = new AlgorithmContext(algorithmInterface);
            except = "StrategyA";
        }

        /// <summary>
        ///DoStrategyAlgorithm 的测试
        ///</summary>
        [TestMethod]
        public void DoStrategyAlgorithmTest()
        {
            actual = algorithmContext.DoStrategyAlgorithm();
            Assert.AreEqual(except, actual);
        }

        [TestCleanup]
        public void CleanUp()
        { 
        
        }
    }
}

测试结构

3.策略模式的优劣:

调用者必须明确地知道每个策略的具体行为和区别,从代码的角度来说,会增加很多策略类。但是好处是对于扩展不需要更改代码,只需要增加模块即可。

[设计模式-04]策略模式-对开发和测试的意义