状态模式(State Pattern)

该文章的最新版本已迁移至个人博客【比特飞】,单击链接 https://www.byteflying.com/archives/425 访问。

状态模式属于行为型模式,它允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。

角色:

1、抽象状态(State)

状态模式的核心基类,它表示某种对象的不同状态;

2、具体状态(Concrete State)

实现抽象状态的具体状态类;

3、环境类(Context)

拥有状态的具体对象,该对象会根据内部不同的对象有不同的行为。

示例:

命名空间StatePattern中包含抽象状态类State,代表水的3种状态,0度及以下时为SolidState固体状态,0到100度为LiquidState液体状态,100度及以上时为GasState气体状态,并且不同的状态可以改变水类Water中喝水Drink方法的行为。本案例尝试以水的3种不同状态来向大家阐述状态模式在实际开发中的应用。

namespace StatePattern
public class Water {

    public State State { get; set; }

    public double Temperature { get; set; } = 0;

    public Water() {
State = new SolidState();
State.Water = this;
} public Water Increase(int value) {
State.Increase(value);
return this;
} public Water Reduce(int value) {
State.Reduce(value);
return this;
} public Water Drink() {
if (this.State is LiquidState) {
Console.WriteLine("You can drink!");
}
else {
Console.WriteLine("You can not drink!");
}
Console.WriteLine(Const.LINE_BREAK);
return this;
} }

Water水类充当环境类,公开一个状态基类,并在内部维护一个温度。Increase调用State的升温,而Reduce调用State的降温,最后的Drink方法会因为水的状态的不同而拥有不同的行为。为了简化逻辑,在本例中,只有当水为液体时才能被饮用。

public abstract partial class State {

    public static Water Water { get; set; }

    protected static string StateName { private get; set; }

    public void Increase(int value) {
if (value == 0) return;
if (value < 0) throw new ArgumentException();
OnStateChanging();
Water._temperature += value;
ChangeState();
} public void Reduce(int value) {
if (value == 0) return;
if (value < 0) throw new ArgumentException();
if (Water._temperature - value <= Const.ABSOLUTE_ZERO) {
throw new UnReachableException();
}
OnStateChanging();
Water._temperature -= value;
ChangeState();
} }

抽象状态基类,首先公开一个水的引用,并在所有实现类中共享StateName状态名,Increase为水升高一个温度,而Reduce为水降温。

public abstract partial class State {

    private void ChangeState() {
if (Water._temperature <= 0) {
Water.State = new SolidState();
}
else if (Water._temperature > 0 && Water._temperature < 100) {
Water.State = new LiquidState();
}
else {
Water.State = new GasState();
}
OnStateChanged();
} protected virtual void OnStateChanging() {
Console.WriteLine(Const.ON_STATE_CHANGING);
Console.WriteLine(
string.Format(Const.TEMPERATURE_INFO,
Water._temperature, StateName));
} protected virtual void OnStateChanged() {
Console.WriteLine(Const.ON_STATE_CHANGED);
Console.WriteLine(
string.Format(Const.TEMPERATURE_INFO,
Water._temperature, StateName));
Console.WriteLine(Const.LINE_BREAK);
} }

抽象状态基类的第2部分(partial ),定义ChangeState方法以在改变温度时更改状态,另外定义OnStateChanging和OnStateChanged这2个受保护的虚方法以便提供“子类可以决定是否重写相应的方法来影响父类”的这样一个功能(OOP特性)。

public class SolidState : State {

    public SolidState() {
StateName = "Solid";
} }
public class LiquidState : State {

    public LiquidState() {
StateName = "Liquid";
} }
public class GasState : State {

    public GasState() {
StateName = "Gas";
} }

水的3种状态的具体实现类,SolidState固体状态、LiquidState液体状态和GasState气体状态,由于我们在状态基类中封装了较多的功能,所以此处的3个具体类都比较精简,只在构造函数中更改共享的StateName状态名称字段。在实际开发过程中,应当尽可能的将具体的功能封装在状态实现类中。

public class Const {

    public const double ABSOLUTE_ZERO = -273.15;

    public const string LINE_BREAK =
"--------------------------------------------------"; public const string ON_STATE_CHANGING = "OnStateChanging()"; public const string ON_STATE_CHANGED = "OnStateChanged()"; public const string TEMPERATURE_INFO = "The temperature is {0} °C" +
" and state name is {1}!"; }

常量类,维护一些在本案例中经常使用到的字符串或数值。在实际开发过程中不应当有此类,应该将相应的常量放在具体要使用的类中。2017年,阿里发布《阿里巴巴Java开发手册》,其中有一节提到此准则,所有使用面向对象编程语言的开发人员都应当遵从。

public class UnReachableException : Exception {

    public UnReachableException()
: base("Absolute zero cannot be reached!") { } public UnReachableException(string message, Exception innerException)
: base(message, innerException) { } }

绝对零度无法到达异常类UnReachableException,进行简单的异常处理。

public class Program {

    private static Water _water = new Water();

    public static void Main(string[] args) {
try {
_water.Increase(68)
.Drink()
.Increase(82)
.Drink()
.Reduce(90)
.Drink()
.Reduce(0)
.Reduce(80)
.Drink()
.Reduce(300)
.Drink();
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
Console.WriteLine(Const.LINE_BREAK);
} Console.ReadKey();
} }

以上是本案例的调用方代码,升温方法Increase、降温方法Reduce和喝水方法Drink经过特别的处理以支持方法链。以下是这个案例的输出结果:

OnStateChanging()
The temperature is 0 °C and state name is Solid!
OnStateChanged()
The temperature is 68 °C and state name is Liquid!
--------------------------------------------------
You can drink!
--------------------------------------------------
OnStateChanging()
The temperature is 68 °C and state name is Liquid!
OnStateChanged()
The temperature is 150 °C and state name is Gas!
--------------------------------------------------
You can not drink!
--------------------------------------------------
OnStateChanging()
The temperature is 150 °C and state name is Gas!
OnStateChanged()
The temperature is 60 °C and state name is Liquid!
--------------------------------------------------
You can drink!
--------------------------------------------------
OnStateChanging()
The temperature is 60 °C and state name is Liquid!
OnStateChanged()
The temperature is -20 °C and state name is Solid!
--------------------------------------------------
You can not drink!
--------------------------------------------------
Absolute zero cannot be reached!
--------------------------------------------------

优点:

该文章的最新版本已迁移至个人博客【比特飞】,单击链接 https://www.byteflying.com/archives/425 访问。

1、封装了转换规则;

2、枚举可能的状态,在枚举状态之前需要确定状态种类;

3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为;

4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块;

5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点:

1、状态模式的使用必然会增加系统类和对象的个数;

2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱;

3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

使用场景:

1、行为随状态改变而改变的场景;

2、条件、分支语句的代替者。

C#设计模式之20-状态模式的更多相关文章

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

    在GOF的<设计模式:可复用面向对象软件的基础>一书中对状态模式是这样说的:允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类.状态模式的重点在于状态转换,很多时候,对 ...

  2. java设计模式-----22、状态模式

    概念: State模式也叫状态模式,是行为设计模式的一种.State模式允许通过改变对象的内部状态而改变对象的行为,这个对象表现得就好像修改了它的类一样. 根据这个概念,我们举个例子 public c ...

  3. 大话设计模式Python实现-状态模式

    状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 下面是一个状态模式的demo: #!/usr/bin/env python # -*- ...

  4. 重学 Java 设计模式:实战状态模式「模拟系统营销活动,状态流程审核发布上线场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! @ 目录 一.前言 二.开发环境 三.状态模式介绍 四.案例场景模拟 1 ...

  5. Java设计模式系列之状态模式

    状态模式(State)的定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它 ...

  6. Java设计模式学习记录-状态模式

    前言 状态模式是一种行为模式,用于解决系统中复杂的对象状态转换以及各个状态下的封装等问题.状态模式是将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象的状态可以灵活多变.这样在客户端使 ...

  7. 《Java设计模式》之状态模式

    状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式. 状态模式同意一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它 ...

  8. JAVA设计模式之【状态模式】

    状态模式 水.固态.气态.液态 账户.正常状态.透支状态.受限状态 状态模式中,用一个状态类来分散冗长的条件语句,让系统有灵活性和可扩展性 状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的 ...

  9. C#设计模式系列:状态模式(State)

    1.状态模式简介 1.1>.定义 状态模式的核心思想是允许一个对象在它的内部状态改变时改变它的行为,即不同的状态对应不同的行为. 状态模式的针对性很强,当有状态变化的时候可以选择状态模式. 1. ...

  10. 【HeadFirst设计模式】10.状态模式

    定义: 允许对象在内部状态改变时改变它 行为,对象看起来好像修改了它的类. OO原则: 封装变化 多用组合,少用继承 针对接口编程,不针对实现编程 为交互对象之间的松耦合设计而努力 类应该对扩展开放, ...

随机推荐

  1. linux 安装superset

    背景说明 公司数据分析人员需要将日常监控分析数据进行可视化,在踩了一些坑之后,终于在业务环境中搭建成功superset,后续复现两次流程也是成功的,分享一波... 业务环境说明 操作系统:centos ...

  2. 当输入一个 URL,实际会发生什么?

    从一个经典的面试题说起 从输入URL到页面展现的过程: 输入URL后,会先进行域名解析.优先查找本地host文件有无对应的IP地址,没有的话去本地DNS服务器查找,还不行的话,本地DNS服务器会去找根 ...

  3. [C++面向对象]-C++成员函数和非成员函数

    大纲: 1.成员函数和非成员函数 2.详细解释 3.总结 4.参考   1.成员函数和非成员函数   其实简单来说成员函数是在类中定义的函数,而非成员函数就是普通函数,即不在类中定义的函数,其中非成员 ...

  4. webpack源码-loader的原理

    版本 webpack :"version": "3.12.0", webpack配置中的loaders配置是如何传递的 webpack/lib/NormalMo ...

  5. 帮助你更好的理解Spring循环依赖

    网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图.流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉得还有一两个坎过不去 ...

  6. Java冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序

    冒泡排序   冒泡排序是一种简单的排序算法.它重复地走访过要排序地数列,一次比较两个元素,如果它们地顺序错误就把它们交换过来.走访数列地工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成. ...

  7. 题解 CF 1372A

    题目 传送门 题意 构造一个长度为n的数组,对于数组中的元素a,b,c,满足\(a+b\neq c\). 思路 直接让数组中的数全部变成1就可以了(其他数也行). 代码 /* * Author :We ...

  8. 图论相关知识(DFS、BFS、拓扑排序、最小代价生成树、最短路径)

    图的存储 假设是n点m边的图: 邻接矩阵:很简单,但是遍历图的时间复杂度和空间复杂度都为n^2,不适合数据量大的情况 邻接表:略微复杂一丢丢,空间复杂度n+m,遍历图的时间复杂度为m,适用情况更广 前 ...

  9. 关于ajaxSubmit传递参数 后台接收为"参数,参数”的问题

    问题: 用户名密码往后台提交的时候,发现接收到的参数变成了下图 解决办法: 去掉ajaxSubmit的data属性 如下图 解释:因为ajaxSubmit在封装的时候会自动的从被form包裹的表单控件 ...

  10. 珍藏多年的学习资料300G+,赶紧免费领取,从此离大神更进一步

    将时间线拉到2014     2014年的寒冬,每天早晨六点钟,都会一个弱小的身影,从学校寝室出发,走在去实习公司的路上.经过食堂边的包子铺,他会顺手买两个包子,一杯豆浆,老板也会像往常一样热情的吆喝 ...