C#设计模式之20-状态模式
状态模式(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-状态模式的更多相关文章
- [设计模式] 20 状态模式 State Pattern
在GOF的<设计模式:可复用面向对象软件的基础>一书中对状态模式是这样说的:允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类.状态模式的重点在于状态转换,很多时候,对 ...
- java设计模式-----22、状态模式
概念: State模式也叫状态模式,是行为设计模式的一种.State模式允许通过改变对象的内部状态而改变对象的行为,这个对象表现得就好像修改了它的类一样. 根据这个概念,我们举个例子 public c ...
- 大话设计模式Python实现-状态模式
状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 下面是一个状态模式的demo: #!/usr/bin/env python # -*- ...
- 重学 Java 设计模式:实战状态模式「模拟系统营销活动,状态流程审核发布上线场景」
作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! @ 目录 一.前言 二.开发环境 三.状态模式介绍 四.案例场景模拟 1 ...
- Java设计模式系列之状态模式
状态模式(State)的定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它 ...
- Java设计模式学习记录-状态模式
前言 状态模式是一种行为模式,用于解决系统中复杂的对象状态转换以及各个状态下的封装等问题.状态模式是将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象的状态可以灵活多变.这样在客户端使 ...
- 《Java设计模式》之状态模式
状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式. 状态模式同意一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它 ...
- JAVA设计模式之【状态模式】
状态模式 水.固态.气态.液态 账户.正常状态.透支状态.受限状态 状态模式中,用一个状态类来分散冗长的条件语句,让系统有灵活性和可扩展性 状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的 ...
- C#设计模式系列:状态模式(State)
1.状态模式简介 1.1>.定义 状态模式的核心思想是允许一个对象在它的内部状态改变时改变它的行为,即不同的状态对应不同的行为. 状态模式的针对性很强,当有状态变化的时候可以选择状态模式. 1. ...
- 【HeadFirst设计模式】10.状态模式
定义: 允许对象在内部状态改变时改变它 行为,对象看起来好像修改了它的类. OO原则: 封装变化 多用组合,少用继承 针对接口编程,不针对实现编程 为交互对象之间的松耦合设计而努力 类应该对扩展开放, ...
随机推荐
- java基础知识--数据类型
计算机时识别不了我们编写的代码语言,计算机中的数据全部采用二进制表示,即0和1表示的数字,每一个0或者1就是一个位,一个位叫做一个bit(比特).(实际上计算机只能识别高低电平,而不是0和1.) 字节 ...
- 《Python测试开发技术栈—巴哥职场进化记》—前言
写在前面 今年从4月份开始写一本讲Python测试开发技术栈的书,主要有两个目的,第一是将自己掌握的一些内容分享给大家,第二是希望自己能系统的梳理和学习Python相关的技术栈.当时我本来打算以故事体 ...
- 如何获取json某一级节点的数据
如何获取json某一级节点的数据 最近做项目有获取和设置固定格式某一级节点值的需求.但是要一级一级地取对于多级的结构来说代码过于冗余且重复,于是写了个递归的方法根据json路径完成值的定点操作.废话不 ...
- vue中使用触摸事件,上滑,下滑,等
第一步,下载一个包 npm install kim-vue-touch -s 在当前项目中下载包 第二部 import vueTouch from 'kim-vue-touch' Vue.use(vu ...
- LQB201803乘积尾零
果然是练思维呀!!要是我的话估计就能挨个算一算呜呜呜 分解成 2和5相乘的式子 #include <iostream> using namespace std; //快速幂运算 int m ...
- PHP gettimeofday() 函数
------------恢复内容开始------------ 实例 返回当前时间: <?php// Print the array from gettimeofday()print_r(gett ...
- PHP debug_zval_dump() 函数
debug_zval_dump 函数用于查看一个变量在zend引擎中的引用计数.类型信息. 版本要求:PHP 4 >= 4.2.0, PHP 5, PHP 7高佣联盟 www.cgewang.c ...
- 一本通 1783 矩阵填数 状压dp 容斥 计数
LINK:矩阵填数 刚看到题目的时候感觉是无从下手的. 可以看到有n<=2的点 两个矩形. 如果只有一个矩形 矩形外的方案数容易计算考虑 矩形内的 必须要存在x这个最大值 且所有值<=x. ...
- 剑指 Offer 57 - II. 和为s的连续正数序列
本题 题目链接 题目描述 我的题解 方法三双100%, 方法一 适合范围广 方法一:双指针(也叫 滑动窗口) 思路分析 用两个指针i和表示当前枚举到的以i为起点,j为终点的区间,sum表示[i,j]的 ...
- HTML与XHTML之间的区别
HTML与XHTML之间的差别,粗略可以分为两大类比较:一个是功能上的差别,另外是书写习惯的差别.关于功能上的差别,主要是XHTML可兼容各大浏览器.手机以及PDA,并且浏览器也能快速正确地编译网页. ...