您的位置:首页 > 编程语言 > C#

[设计模式] 状态模式-C#

2009-08-06 22:35 423 查看
状态模式主要解决当控制一个对象状态的转换的条件表达过于复杂的情况,使得状态的转换不依赖于整体的操作。本文将通过一个具体的例子说明状态模式的应用。假设下面一个场景:

     一个新任务提交后,先是收集数据,数据收集完成后等等分配一台机器,分配到机器后就可以将此任务部署至此机器后就可以通知相关模块开始工作。

 

先对此场景做分析如下:

   一个任务共分四个步骤:一收集数据,二分配机器,三部署到测试机,四通知相关对象。

   任务的状态可以分为:new, waiting, Ready, Running分别对应新任务提交,等待分配机器,准备部署,和部署完成于上面的步骤一一对应。

 

 

定义一个任务类:

 /*abstract*/ class Task : IGatherData, IAllotMachine, IDeployTestManchine, INotifyUser   // 这里是分别为以上四个步骤定义的接口
    {

        // 任务在数据库里对应的ID
        private string taskIdInDataTable;
        public string TaskIdInDataTable
        {
            get
            {
                return taskIdInDataTable;
            }
            set
            {
                taskIdInDataTable = value;
            }
        }

 

        // 任务的状态

        private TaskState _TaskState;
        public TaskState TaskState
        {
            get
            {
                return _TaskState;
            }

            set
            {
                _TaskState = value;
                Log.Info("当前状态:" + _TaskState.GetType().Name);
            }
        }

 

        public Task()
        {
            this._TaskState = new NewTaskState();
        }

 

       // 可以指定一个任务的当前状态

        public UpDateTask(TaskState taskTask)
        {
            this._TaskState = taskTask;
        }

 

        /// <summary> 改变一个任务的状态至当前任务的下一个状态
        /// </summary>
        public void ChangeState()
        {
            System.Threading.Thread.Sleep(500);
            this._TaskState.ChangeState(this);
        }

 

        /// 以下是实现接口中的方法       
        /// 合并数据
        public virtual bool gatherData()
        {

            return true;
        }

        /// 分配机器
        /// 分配机器放在基类里面做,因为这个动作对每个任务来说基本都是一样的
        public virtual bool allotMachine()
        {
            DoWork work = new DoWork();

            return work.allotMachine(this.taskIdInDataTable);
        }

        /// 部署至测试机
        /// 部署至测试机放在基类里面做,因为这个动作对每个任务来说基本都是一样的
        public virtual bool deployTestManchine()
        {
            DoWork work = new DoWork();

            return work.deployTestManchine(this.taskIdInDataTable);
        }

 

        /// 通知相关人员

        /// 通知相关人员放在基类里面做,因为这个动作对每个任务来说基本都是一样的
        public virtual bool deployTestManchine()
        {
            DoWork work = new DoWork();

            return work.deployTestManchine(this.taskIdInDataTable);
        }

    }

 

到此我们的任务类已经定义完成,其中有一个ChangeState的方法,此方法负责调用状态类的改变状态的方法。

 

接下面就写Status类:

先是定义一个超类,此类只有一个方法就是ChangeState, 此方法负责控制任务状态的转换。

abstract class TaskState
    {
        /// <summary>状态改变过程
        /// </summary>状态的改变由统一的接口进行从一个状态到下一下状态的改变,
        ///           使用时不用关心内部的状态是怎么改变的,只需统一调用此方法即可。
        /// <param name="task"></param>
        public abstract void ChangeState(UpDateTask task);
    }

 

下面定义具体的状态类:

// 新任务状态
    class NewTaskState: TaskState
    {
        public override void ChangeState(UpDateTask task)
        {
            // 检查当前状态
            if (this == task.TaskState)
            {               
                if (task.gatherData())
                {
                    task.TaskState = new WaitingTaskState(task);
                }
                else // 如果合并数据任务失败,将任务打到已清除状态
                {
                    task.TaskState = new DeletedState(task);
                }
            }           
        }

        public NewTaskState()
        {

        }

        public NewTaskState(UpDateTask task)
        {
            DoWork.setTaskState(task.TaskIdInDataTable,  enumTaskState.NewTask);
        }
    }

    // 等待任务状态
    class WaitingTaskState : TaskState
    {
        public override void ChangeState(UpDateTask task)
        {
            if (this == task.TaskState)
            {
                if (task.allotMachine())
                {
                    task.TaskState = new ReadyTaskState(task);
                }
                else
                {
                    task.TaskState = new WaitingTaskState(task);                  
                }
            }           
        }

        public WaitingTaskState()
        {
          
        }

        public WaitingTaskState(UpDateTask task)
        {
            DoWork.setTaskState(task.TaskIdInDataTable, enumTaskState.Waiting);          
        }

    }

    // 准备任务状态
    class ReadyTaskState : TaskState
    {
        public override void ChangeState(UpDateTask task)
        {
            if (this == task.TaskState)
            {
                if (task.deployTestManchine())
                {
                    task.TaskState = new RunningState(task);
                }
                else
                {
                    // 部署失败则重新分配新的测试机器
                    task.TaskState = new WaitingTaskState(task);
                }
            }           
        }

        public ReadyTaskState()
        {
           
        }

        public ReadyTaskState(UpDateTask task)
        {
            DoWork.setTaskState(task.TaskIdInDataTable, enumTaskState.Ready);           
        }

      
    }

    // 运行任务状态
    class RunningState : TaskState
    {
        public override void ChangeState(UpDateTask task)
        {         
            // send e-mail       
           
        }    
  
       
        public RunningState()
        {
           
        }

        public RunningState(UpDateTask task)
        {
            DoWork.setTaskState(task.TaskIdInDataTable, enumTaskState.Running);
        }
    }

 

 // 下来看客户端调用

Task  task = new Task ();

task.TaskIdInDataTable = 1111;  // 为此任务指定一个ID

 

下来就最重要的,做了这么多事就是为了下面的调用:

task.ChangeState();

task.ChangeState();

task.ChangeState();

task.ChangeState();

这样此任务就完成了,客户端根本不用关心任务的状态是怎么转换的,因为每一个状态的转换都是由当前的状态决定自己的下一个状态是什么,即使要修改状态的流程也只需要修改状态类就可以,也不用关心具体的操作。这样做很方便的将状态转换、任务的具体步骤、与客户端的调用分开,之间不受影响。

 

也可以指定一个任务的当前状态,从此状态开始,比如可以指定此任务从Wating开始,此任务就会从Waiting对应的allotMachine开始运行:

Task  task = new Task (new WaitingTaskState);

 

到此状态模式的应该已全部写完。^_^

 

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息