深入浅出设计模式——状态模式(State Pattern)

模式动机

在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的 (stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化。
在UML中可以使用状态图来描述对象状态的变化。

模式定义
状态模式(State Pattern) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。
State Pattern: Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
Frequency of use: medium
UML图

模式结构
状态模式包含如下角色:
Context: 环境类
State: 抽象状态类
ConcreteState: 具体状态类

模式分析
状态模式描述了对象状态的变化以及对象如何在每一种状态下表现出不同的行为。
状态模式的关键是引入了一个抽象类来专门表示对象的状态,这个类我们叫做抽象状态类,而对象的每一种具体状态类都继承了该类,并在不同具体状态类中实现了不同状态的行为,包括各种状态之间的转换

在状态模式结构中需要理解环境类与抽象状态类的作用:
? 环境类实际上就是拥有状态的对象,环境类有时候可以充当状态管理器(State Manager)的角色,可以在环境类中对状态进行切换操作。
? 抽象状态类可以是抽象类,也可以是接口,不同状态类就是继承这个父类的不同子类,状态类的产生是由于环境类存在多个状态,同时还满足两个条件:这些状态经常需要切换,在不同的状态下对象的行为不同。因此可以将不同对象下的行为单独提取出来封装在具体的状态类中,使得环境类对象在其内部状态改变时可以改变它的行为,对象看起来似乎修改了它的类,而实际上是由于切换到不同的具体状态类实现的。由于环境类可以设置为任一具体状态类,因此它针对抽象状态类进行编程,在程序运行时可以将任一具体状态类的对象设置到环境类中,从而使得环境类可以改变内部状态,并且改变行为。

模式实例与解析
无尽加班何时休—状态模式示例代码
体系结构

Context: 环境类  Work.cs

namespace StatePattern
{
    public class Work
    {
        private State current;
        public Work()
        {
            //工作状态初始化为上午工作状态
            current = new ForenoonState();
        }
        private double hour;
        public double Hour
        {
            get { return hour; }
            set { hour = value; }
        }
        private bool finish;
        public bool TaskFinished
        {
            get { return finish; }
            set { finish = value; }
        }
        public void SetState(State s)
        {
            current = s;
        }
        public void WriteProgram()
        {
            current.WriteProgram(this);
        }
    }
}

State: 抽象状态类 State.cs

namespace StatePattern
{
    //抽象状态
    public abstract class State
    {
        public abstract void WriteProgram(Work w);
    }
}

ConcreteState: 具体状态类 上午工作状态 ForenoonState.cs

using System;

namespace StatePattern
{
    //上午工作状态
    public class ForenoonState : State
    {
        public override void WriteProgram(Work w)
        {
            if (w.Hour < 12)
            {
                Console.WriteLine("当前时间:{0} 点上午工作,精神百倍", w.Hour);
            }
            else
            {
                //超过12点,则转入中午工作状态
                w.SetState(new NoonState());
                w.WriteProgram();
            }
        }
    }
}

中午工作状态 NoonState.cs

using System;

namespace StatePattern
{
    //中午工作状态
    public class NoonState : State
    {
        public override void WriteProgram(Work w)
        {
            if (w.Hour < 13)
            {
                Console.WriteLine("当前时间:{0} 点饿了,午饭:犯困,午休。", w.Hour);
            }
            else
            {
                //超过13点,则转入下午工作状态
                w.SetState(new AfternoonState());
                w.WriteProgram();
            }
        }
    }
}

下午工作状态 AfternoonState.cs

using System;

namespace StatePattern
{
    //下午工作状态
    public class AfternoonState : State
    {
        public override void WriteProgram(Work w)
        {
            if (w.Hour < 17)
            {
                Console.WriteLine("当前时间:{0}点 下午状态还不错,继续努力", w.Hour);
            }
            else
            {
                //超过17点,则转入傍晚工作状态
                w.SetState(new EveningState());
                w.WriteProgram();
            }
        }
    }
}

晚间工作状态 EveningState.cs

using System;

namespace StatePattern
{
    //晚间工作状态
    public class EveningState : State
    {
        public override void WriteProgram(Work w)
        {
            if (w.TaskFinished)
            {
                //如果完成任务,则转入下班状态
                w.SetState(new ResetState());
                w.WriteProgram();
            }
            else
            {
                if (w.Hour < 21)
                {
                    Console.WriteLine("当前时间:{0} 加班哦,疲累之极", w.Hour);
                }
                else
                {
                    //超过21点,则转入睡眠工作状态
                    w.SetState(new SleepingState());
                    w.WriteProgram();
                }
            }
        }
    }
}

睡眠状态 SleepingState.cs

using System;

namespace StatePattern
{
    //睡眠状态
    public class SleepingState : State
    {
        public override void WriteProgram(Work w)
        {
            Console.WriteLine("当前时间:{0}点不行了,睡着了", w.Hour);
        }
    }
}

下午休息状态 ResetState.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace StatePattern
{
    //下午休息状态
    public class ResetState : State
    {
        public override void WriteProgram(Work w)
        {
            Console.WriteLine("当前时间:{0}点下班回家了", w.Hour);
        }
    }
}

Client:客户类

using System;

namespace StatePattern
{
    class Program
    {
        static void Main(string[] args)
        {
            //紧急项目
            Work emergencyProjects = new Work();
            emergencyProjects.Hour = 9;
            emergencyProjects.WriteProgram();
            emergencyProjects.Hour = 10;
            emergencyProjects.WriteProgram();
            emergencyProjects.Hour = 12;
            emergencyProjects.WriteProgram();
            emergencyProjects.Hour = 13;
            emergencyProjects.WriteProgram();
            emergencyProjects.Hour = 14;
            emergencyProjects.WriteProgram();
            emergencyProjects.Hour = 17;
            emergencyProjects.TaskFinished = false;
            emergencyProjects.WriteProgram();

            emergencyProjects.Hour = 19;
            emergencyProjects.WriteProgram();
            emergencyProjects.Hour = 22;
            emergencyProjects.WriteProgram();
            Console.Read();
        }
    }
}

模式优缺点
状态模式的优点
? 封装了转换规则。
? 枚举可能的状态,在枚举状态之前需要确定状态种类。
? 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
? 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
? 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

状态模式的缺点
? 状态模式的使用必然会增加系统类和对象的个数。
? 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
? 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

模式适用环境
在以下情况下可以使用状态模式:
? 对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。
? 代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态。

【声明与感谢】
本文,站在许多巨人的肩膀上,借鉴和引用了许多他人拥有版权的作品或著述,在此,对前人们的贡献致谢。并同时公布引用的内容、原作者或来源(一些来源于互联网的内容本人无法追述本源,深表遗憾)。

【参考文献】
《设计模式—可复用面向对象软件的基础》作者: [美] Erich Gamma / Richard Helm / Ralph Johnson / John Vlissides 译者: 李英军 / 马晓星 / 蔡敏 / 刘建中 等 机械工业出版社
《重构—改善既有代码的设计》作者: Martin Fowler译者:候捷 中国电力出版社
《敏捷软件开发—原则、模式与实践》作者: Robert C. Martin 清华大学出版社
《程序员修炼之道—从小工到专家》作者: Andrew Hunt / David Thomas 电子工业出版社
《Head First 设计模式》作者: 弗里曼 译者: O‘Reilly Taiwan公司 中国电力出版社
《设计模式之禅》 作者: 秦小波 机械工业出版社
MSDN WebCast 《C#面向对象设计模式纵横谈》 讲师:李建忠
刘伟. 设计模式. 北京:清华大学出版社, 2011.
刘伟. 设计模式实训教程. 北京:清华大学出版社, 2012.
《大话设计模式》 作者: 程杰 清华大学出版社
《C#图解教程》作者: 索利斯 译者: 苏林 / 朱晔 人民邮电出版社
《你必须知道的.NET》作者: 王涛
《项目中的.NET》作者: 李天平 电子工业出版社
《Microsoft .NET企业级应用架构设计》作者: (美)埃斯波西托等编著 译者: 陈黎夫
http://www.dofactory.com/Patterns/Patterns.aspx .NET Design Patterns
http://www.cnblogs.com/zhenyulu 博客作者:吕震宇
http://www.cnblogs.com/terrylee 博客作者:李会军
http://www.cnblogs.com/anlyren/ 博客作者:anlyren
http://www.cnblogs.com/idior 博客作者:idior
http://www.cnblogs.com/allenlooplee 博客作者:Allen lee
http://blog.csdn.net/ai92 博客作者:ai92
http://www.cnblogs.com/umlonline/ 博客作者:张传波
http://www.cnblogs.com/lovecherry/ 博客作者:LoveCherry

时间: 2024-09-30 15:45:09

深入浅出设计模式——状态模式(State Pattern)的相关文章

设计模式 - 状态模式(state pattern) 未使用状态模式 详解

状态模式(state pattern) 未使用状态模式 详解 本文地址: http://blog.csdn.net/caroline_wendy 状态模式可以控制状态的转换, 未使用设计模式时, 程序会非常繁杂. 具体方法: 1. 状态转换类. /** * @time 2014年7月11日 */ package state; /** * @author C.L.Wang * */ public class GumballMachine { final static int SOLD_OUT =

设计模式 - 状态模式(state pattern) 详解

状态模式(state pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy 状态模式(state pattern): 允许对象在内部状态改变时改变它的行为, 对象看起来好像修改了它的类. 建立Context类, 包含多个具体状态(concrete state)类的组合, 根据状态的不同调用具体的方法, state.handle(), 包含set\get方法改变状态. 状态接口(state interface), 包含抽象方法handle(),

设计模式(行为型)之状态模式(State Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(行为型)之模板方法模式(Template Method Pattern)>http://blog.csdn.net/yanbober/article/details/45501715 概述 状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题.当系统中

[设计模式] 20 状态模式 State Pattern

在GOF的<设计模式:可复用面向对象软件的基础>一书中对状态模式是这样说的:允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类.状态模式的重点在于状态转换,很多时候,对于一个对象的状态,我们都是让这个对象包含一个状态的属性,这个状态属性记录着对象的具体状态,根据状态的不同使用分支结构来执行不同的功能,就像上面的代码那样处理:就像上面说的,类中存在大量的结构类似的分支语句,变得难以维护和理解.状态模式消除了分支语句,就像工厂模式消除了简单工厂模式的分支语句一样,将状态处理分散

状态模式(State Pattern)

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化. 上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理. 抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为. 具体状态(Concrete State

设计模式之状态模式(State)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

设计模式 - 代理模式(proxy pattern) 未使用代理模式 详解

代理模式(proxy pattern) 未使用代理模式 详解 本文地址: http://blog.csdn.net/caroline_wendy 部分代码参考: http://blog.csdn.net/caroline_wendy/article/details/37698747 如果需要监控(monitor)类的某些状态, 则需要编写一个监控类, 并同过监控类进行监控. 但仅仅局限于本地, 如果需要远程监控, 则需要使用代理模式(proxy pattern). 具体方法: 1. 类中需要提供

设计模式 状态模式 以自动售货机为例

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/26350617 状态模式给了我眼前一亮的感觉啊,值得学习~ 先看定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类.定义又开始模糊了,理一下,当对象的内部状态改变时,它的行为跟随状态的改变而改变了,看起来好像重新初始化了一个类似的. 下面使用个例子来说明状态模式的用法,现在有个自动售货机的代码需要我们来写,状态图如下: 分析一个这个状态图: a.包含4个状态(

设计模式 - 命令模式(command pattern) 撤销(undo) 详解

命令模式(command pattern) 撤销(undo) 详解 本文地址: http://blog.csdn.net/caroline_wendy 参考命令模式: http://blog.csdn.net/caroline_wendy/article/details/31379977 命令模式可以用于执行撤销(undo)操作. 具体方法: 1. 对象类中需要保存状态, 如level. package command; public class CeilingFan { String loca