状态模式

假设某人一整天的流程就是“吃饭→工作→睡觉”,并且他要严格按照这个流程来,那也就是说在吃饭状态下,他只能做跟吃饭有关的事情,而不能越界做工作或者睡觉有关的事情,这就涉及到了状态的管理。在写代码的时候,我们常常要根据当前的状态,来决定下一个状态并且确定当前状态下要做什么样的事情。一般来讲,我们会写一系列的if-else if-else if-else语句来实现,但是要是状态非常多的时候,这种代码不仅仅看起来十分长,而且看的人生理上也会有一种厌恶感并且看一点估计忘一点。导致这种臭代码的原因就是,我们一口气在一个函数里面管理了太多种的状态。于是,有人就提出了,能不能让状态自己管理自己。也就有点像我们的行政制度中自治区,我们不再统一地在一个函数里面管理N种状态,而是让某个状态管理它自己本身。

经典的状态管理模式如下图所示

image

让我来解释一下这幅图的意思,IState依赖于IContext,也就是IContext对应的实体类拥有一个IState类型的字段,在IContext类里面的Request()方法里面调用IState中的Handle方法。我们可以通过改变IContext里面的IState实体类类型来改变状态,从而实现根据不同的状态使用不同的方法。

     下面让我们来段代码说明一下。以下代码模拟数据库的连接、关闭。连接和关闭两个状态分别有两个对应的状态类与之对应。

1、首先我们还是先定义状态的接口,另外定义一个抽象类,用来表示数据库连接类。

/// <summary>
    /// 数据库连接状态
    /// </summary>
    public interface IConnectionState
    {
        void Open();
        void Close();
        void Query();
    }

在定义一个数据库连接对象的抽象基类

abstract class ContextBase
    {

        private IConnectionState connectionState;

        public IConnectionState ConnectionState
        {
            get { return connectionState; }
            set { connectionState = value; }
        }

        public virtual void Open() { this.connectionState.Open(); }
        public virtual void Close() { this.connectionState.Close(); }
        public virtual void Query() { this.connectionState.Query(); }
    }

2、我们再定义两个具体类OpenState、CloseState,实现IConnectionState接口,分别代表数据库开、关两种状态。并且在状态类内部,如OpenState类的内部规定了在Opne状态下,可以执行的操作和不能够执行的操作。

/// <summary>
        /// 打开状态
        /// </summary>
        class OpenState : IConnectionState
        {

            public void Open()
            {
                throw new NotImplementedException("打开状态下,不能再打开数据库连接");
            }

            public void Close()
            {
                //打开状态下,可以关闭数据库连接
            }

            public void Query()
            {
                //打开状态下,可以查询
            }
        }
        /// <summary>
        /// 关闭状态
        /// </summary>
        class CloseState : IConnectionState
        {

            public void Open()
            {
                //关闭状态下可以重新打开连接
            }

            public void Close()
            {
                throw new NotImplementedException("关闭状态下,不能再关闭数据库连接");
            }

            public void Query()
            {
                throw new NotImplementedException("关闭状态下,不能查询数据库");
            }
        }

3、下面我们用一个类来继承抽象类,并进行测试。具体的类实现方式见下方,这里只给出关键函数。

public void ConnectionStateTest()
        {
            ContextBase target = CreateContextBase(); 
            target.ConnectionState = new OpenState();
            try
            {
                target.Open();//执行Open操作,遇到异常,不执行 Assert.IsTrue(false);
                Assert.IsTrue(false);
            }
            catch (Exception ex)
            {
                
            }
            target.Close();//在打开状态下,正常关闭,并执行Assert.IsTrue(false);
            Assert.IsTrue(false);

            target.ConnectionState = new CloseState();
            try
            {
                target.Close();
                Assert.IsTrue(false);
            }
            catch (Exception ex)
            {
            }

            target.Open();
            Assert.IsTrue(false);
        }

从以上代码,我们可以知道,原先需要用讨厌的if-else if-else if-else管理的讨厌的长长的状态管理函数,已经被我们封装在一个个的小状态管理类里面了,并且这些状态管理类可以自己管理自己什么状态应该做什么样的事情。

  1 using BangWorks.PractcalPattern.States.Classic;
  2 using Microsoft.VisualStudio.TestTools.UnitTesting;
  3 using System;
  4 
  5 namespace BangWork.PractcalPattern.Concept.InderTest
  6 {
  7 
  8 
  9     /// <summary>
 10     ///这是 ContextBaseTest 的测试类,旨在
 11     ///包含所有 ContextBaseTest 单元测试
 12     ///</summary>
 13     [TestClass()]
 14     public class ContextBaseTest : ContextBase
 15     {
 16         /// <summary>
 17         /// 打开状态
 18         /// </summary>
 19         class OpenState : IConnectionState
 20         {
 21 
 22             public void Open()
 23             {
 24                 throw new NotImplementedException("打开状态下,不能再打开数据库连接");
 25             }
 26 
 27             public void Close()
 28             {
 29                 //打开状态下,可以关闭数据库连接
 30             }
 31 
 32             public void Query()
 33             {
 34                 //打开状态下,可以查询
 35             }
 36         }
 37         /// <summary>
 38         /// 关闭状态
 39         /// </summary>
 40         class CloseState : IConnectionState
 41         {
 42 
 43             public void Open()
 44             {
 45                 //关闭状态下可以重新打开连接
 46             }
 47 
 48             public void Close()
 49             {
 50                 throw new NotImplementedException("关闭状态下,不能再关闭数据库连接");
 51             }
 52 
 53             public void Query()
 54             {
 55                 throw new NotImplementedException("关闭状态下,不能查询数据库");
 56             }
 57         }
 58         private TestContext testContextInstance;
 59 
 60         /// <summary>
 61         ///获取或设置测试上下文,上下文提供
 62         ///有关当前测试运行及其功能的信息。
 63         ///</summary>
 64         public TestContext TestContext
 65         {
 66             get
 67             {
 68                 return testContextInstance;
 69             }
 70             set
 71             {
 72                 testContextInstance = value;
 73             }
 74         }
 75 
 76         #region 附加测试特性
 77         // 
 78         //编写测试时,还可使用以下特性:
 79         //
 80         //使用 ClassInitialize 在运行类中的第一个测试前先运行代码
 81         //[ClassInitialize()]
 82         //public static void MyClassInitialize(TestContext testContext)
 83         //{
 84         //}
 85         //
 86         //使用 ClassCleanup 在运行完类中的所有测试后再运行代码
 87         //[ClassCleanup()]
 88         //public static void MyClassCleanup()
 89         //{
 90         //}
 91         //
 92         //使用 TestInitialize 在运行每个测试前先运行代码
 93         //[TestInitialize()]
 94         //public void MyTestInitialize()
 95         //{
 96         //}
 97         //
 98         //使用 TestCleanup 在运行完每个测试后运行代码
 99         //[TestCleanup()]
100         //public void MyTestCleanup()
101         //{
102         //}
103         //
104         #endregion
105 
106 
107         internal virtual ContextBase CreateContextBase()
108         {
109             // TODO: 实例化相应的具体类。
110             ContextBase target = new ContextBaseTest();
111             return target;
112         }
113 
114         /// <summary>
115         ///Close 的测试
116         ///</summary>
117         [TestMethod()]
118         public void CloseTest()
119         {
120             ContextBase target = CreateContextBase(); // TODO: 初始化为适当的值
121             target.Close();
122             Assert.Inconclusive("无法验证不返回值的方法。");
123         }
124 
125         /// <summary>
126         ///Open 的测试
127         ///</summary>
128         [TestMethod()]
129         public void OpenTest()
130         {
131             ContextBase target = CreateContextBase(); // TODO: 初始化为适当的值
132             target.Open();
133             Assert.Inconclusive("无法验证不返回值的方法。");
134         }
135 
136         /// <summary>
137         ///Query 的测试
138         ///</summary>
139         [TestMethod()]
140         public void QueryTest()
141         {
142             ContextBase target = CreateContextBase(); // TODO: 初始化为适当的值
143             target.Query();
144             Assert.Inconclusive("无法验证不返回值的方法。");
145         }
146 
147         /// <summary>
148         ///ConnectionState 的测试
149         ///</summary>
150         [TestMethod()]
151         public void ConnectionStateTest()
152         {
153             ContextBase target = CreateContextBase(); // TODO: 初始化为适当的值
154 
155             target.ConnectionState = new OpenState();
156             try
157             {
158                 target.Open();//执行Open操作,遇到异常,不执行 Assert.IsTrue(false);
159                 Assert.IsTrue(false);
160             }
161             catch (Exception ex)
162             {
163                 
164             }
165             target.Close();//在打开状态下,正常关闭,并执行Assert.IsTrue(false);
166             Assert.IsTrue(false);
167 
168             target.ConnectionState = new CloseState();
169             try
170             {
171                 target.Close();
172                 Assert.IsTrue(false);
173             }
174             catch (Exception ex)
175             {
176             }
177 
178             target.Open();
179             Assert.IsTrue(false);
180         }
181     }
182 }
状态管理类完整代码
posted @ 2013-08-27 19:49  陈哈哈  阅读(368)  评论(0编辑  收藏  举报