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原则: 封装变化 多用组合,少用继承 针对接口编程,不针对实现编程 为交互对象之间的松耦合设计而努力 类应该对扩展开放, ...
随机推荐
- Python | Python初学者的自我修养,找到自己的方向
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是Python专题的第21篇文章,我们继续多线程的话题. 上周的文章当中我们简单介绍了线程和进程的概念,以及在Python当中如何在主线 ...
- OGG19.1 oracle12c到oracle12c经典模式配置实施
OGG19.1 oracle12c到oracle12c经典和集成模式配置实施 目的说明 本文提供Oracle GoldenGate在Oracle db到Oracle db的数据复制安装配置指导,适用于 ...
- 【软件安装】CentOS7安装MariaDb(mysql_替代品安装)
1.背景 Maria Db是流行的跨平台MySQL数据库管理系统的分支,被认为是MySQL 的完全替代品.Maria Db是由Sun在Sun Micro systems合并期间被Oracle收购后,于 ...
- MacOS系统隐藏文件操作
显示或关闭隐藏文件 显示:defaults write com.apple.finder AppleShowAllFiles -bool true ; killall Finder隐藏:default ...
- SQL数据多条转单条(CONCAT_WS)
一.concat()函数可以连接一个或者多个字符串 concat(str1,str2,…) 返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. select conc ...
- Region Normalization for Image Inpainting, AAAI 2020
论文:Region Normalization for Image Inpainting, AAAI 2020 代码:https://github.com/geekyutao/RN 图像修复的目的是重 ...
- php提取xml配置参数
demo1.php <?php class AddressManager{ private $addresses = array("ip地址1","ip地址2&qu ...
- MacOS下ElasticSearch学习(第一天)
ElasticSearch第一天 学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"elasticsearch&q ...
- luogu CF125E MST Company wqs二分 构造
LINK:CF125E MST Company 难点在于构造 前面说到了求最小值 可以二分出斜率k然后进行\(Kruskal\) 然后可以得到最小值.\(mx\)为值域. 得到最小值之后还有一个构造问 ...
- RabbitMQ学习总结(2)-API的使用
1. 引用依赖 <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-clie ...