c#设计模式系列:状态模式(State pattern)
引言
我们在编程的时候,有时候会遇到,一个对象的行为动作会由对象的状态来决定的,也就是对象的行为是由状态来决定,如果对象的状态很多,那么也会由很多不同的行为,这时候我们一班会 if –else if—来判断对象的行为,当对象的行为或者状态发生变化时,就需要更改之前的代码,这样的设计就违背了开闭原则,而状态模式就是用来解决这样的问题的
状态模式的介绍
- 状态模式的定义
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像改变了其类
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况,把状态的判断逻辑转移到表示不同状态的一系列类中,可以把负责的判断逻辑简单化,如果这个状态判断很简单,就没毕业使用“状态模式”了。
- 状态模式的结构图

- 状态模式的组成
(1)、环境角色(Context):也称上下文,定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
(2)、抽象状态角色(State):定义一个接口,用以封装环境对象的一个特定的状态所对应的行为。
(3)、具体状态角色(ConcreteState):每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。
- 状态模式的代码实现
比如:支付宝中蚂蚁会员等级来说明,分为:大众会员、黄金会员、铂金会员、钻石会员四种, 不同的等级享受的服务不同,我们就拿免费体现的额度来比较一下,钻石会员体现的额度是100万,其他都是2万的额度,感觉这差别太大了,当额度使用完以后,可以使用蚂蚁积分兑换,兑换的规则是 大众会员 1积分兑换1元体现额度,黄金会员1积分兑换1.5的额度,铂金1积分兑换3元,钻石1积分兑换5元
第一版
class Program
{
static void Main(string[] args)
{
Member m = new Member();
m.Membership = "大众会员";
m.Lines = true;
m.Withdrawal();
m.Membership = "黄金会员";
m.Lines = false;
m.Withdrawal();
m.Membership = "铂金会员";
m.Lines = true;
m.Withdrawal();
m.Membership = "钻石会员";
m.Lines = true;
m.Withdrawal();
}
}
public class Member
{
public string Membership { get; set; } //会员等级
public bool Lines { get; set; } //免费体现额度 public void Withdrawal()
{
if (Membership=="大众会员")
{
if (Lines)
{
Console.WriteLine("大众会员1积分可以兑换1元提现额度");
}
else
{
Console.WriteLine("您还有免费的提现额度");
}
}
else if (Membership=="黄金会员")
{
if (Lines)
{
Console.WriteLine("黄金1积分可以兑换1.5元提现额度");
}
else
{
Console.WriteLine("您还有免费的提现额度");
}
}
else if (Membership=="铂金会员")
{
if (Lines)
{
Console.WriteLine("铂金会员1积分可以兑换3元提现额度");
}
else
{
Console.WriteLine("您还有免费的提现额度");
}
}
else
{
if (Lines)
{
Console.WriteLine("钻石1积分可以兑换5元提现额度");
}
else
{
Console.WriteLine("您有100的免费提现额度");
}
}
}
}
现在功能实现了,但是看仔细看看Member类中的Withdrawal方法,里面有很大分子判断,这就说明它的责任过大了,无论是任何状态,都需要通过它来改变,明显违背了“单一职责原则”、“开发封闭原则”。
第二版
class Program
{
static void Main(string[] args)
{
Context m = new Context();
m.Action();
}
} public abstract class State
{
public abstract void Handle(Context context);
} public class PublicMember:State
{
public override void Handle(Context context)
{
if (context.Lines)
{
Console.WriteLine("大众会员1积分可以兑换1元提现额度");
}
else
{
Console.WriteLine("您还有免费的提现额度");
}
context.SetState(new GoldMember());
context.Lines = true;
context.Action();
}
} public class GoldMember:State
{
public override void Handle(Context context)
{
if (context.Lines)
{
Console.WriteLine("黄金1积分可以兑换1.5元提现额度");
}
else
{
Console.WriteLine("您还有免费的提现额度");
}
context.SetState(new PlatinumMember());
context.Lines = true;
context.Action();
}
} public class PlatinumMember : State
{
public override void Handle(Context context)
{
if (context.Lines)
{
Console.WriteLine("铂金会员1积分可以兑换3元提现额度");
}
else
{
Console.WriteLine("您还有免费的提现额度");
}
context.SetState(new DiamondMember());
context.Lines = true;
context.Action();
}
} public class DiamondMember : State
{
public override void Handle(Context context)
{
if (context.Lines)
{
Console.WriteLine("钻石1积分可以兑换5元提现额度");
}
else
{
Console.WriteLine("您有100的免费提现额度");
}
}
}
public class Context
{
public Context()
{
state = new PublicMember();
Lines = true;
}
private State state;
public string Membership { get; set; } //会员等级
public bool Lines { get; set; } //免费体现额度
public void SetState(State s)
{
state = s;
}
public void Action()
{
state.Handle(this);
}
}
状态模式的优缺点
- 状态模式的优点
(1)、封装了转换规则。
(2)、枚举可能的状态,在枚举状态之前需要确定状态种类。
(3)、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
(4)、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
(5)、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。 - 状态模式的缺点
(1)、状态模式的使用必然会增加系统类和对象的个数。
(2)、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
(3)、状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
总结
在对象的行动取决于本身的状态时,可以适用于状态模式,免去了过多的if–else判断,这对于一些复杂的和繁琐的判断逻辑有很好的帮助。但是使用状态模式,势必会造成更多的接口和类,对于非常简单的状态判断,可以不使用
c#设计模式系列:状态模式(State pattern)的更多相关文章
- 乐在其中设计模式(C#) - 状态模式(State Pattern)
原文:乐在其中设计模式(C#) - 状态模式(State Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 状态模式(State Pattern) 作者:webabcd 介绍 允 ...
- 二十四种设计模式:状态模式(State Pattern)
状态模式(State Pattern) 介绍允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它所属的类. 示例有一个Message实体类,对它的操作有Insert()和Get()方法, ...
- [设计模式] 20 状态模式 State Pattern
在GOF的<设计模式:可复用面向对象软件的基础>一书中对状态模式是这样说的:允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类.状态模式的重点在于状态转换,很多时候,对 ...
- 【UE4 设计模式】状态模式 State Pattern
概述 描述 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类. 其别名为状态对象(Objects for States),状态模式是一种对象行为型模式. 有限状态机(FSMs) ...
- 【转】设计模式 ( 十七) 状态模式State(对象行为型)
设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...
- 设计模式 ( 十七) 状态模式State(对象行为型)
设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...
- 状态模式-State Pattern(Java实现)
状态模式-State Pattern 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. State接口 ...
- 北风设计模式课程---状态模式State(对象行为型)
北风设计模式课程---状态模式State(对象行为型) 一.总结 一句话总结: 状态模式 具体状态的行为在具体的状态类中就解决,不用交给外部做判断.实质是将多条件判断弄成了多个类,在不同的类中做判断 ...
- 设计模式2——状态模式State
参考链接: 设计模式之状态模式:https://www.cnblogs.com/haoerlv/p/7777789.html 设计模式系列之状态模式:https://www.jianshu.com/p ...
- 设计模式(十二):通过ATM取款机来认识“状态模式”(State Pattern)
说到状态模式,如果你看过之前发布的重构系列的文章中的<代码重构(六):代码重构完整案例>这篇博客的话,那么你应该对“状态模式”并不陌生,因为我们之前使用到了状态模式进行重构.上一篇博客我们 ...
随机推荐
- leetcode260
public class Solution { public int[] SingleNumber(int[] nums) { var dic = new Dictionary<int, int ...
- 【源码阅读】Java集合之二 - LinkedList源码深度解读
Java 源码阅读的第一步是Collection框架源码,这也是面试基础中的基础: 针对Collection的源码阅读写一个系列的文章; 本文是第二篇LinkedList. ---@pdai JDK版 ...
- Linux重置管理员密码
对于一些非专业的Linux运维工程师或者偶尔使用Linux系统的学习者而言,比如我,经常会碰到忘记密码的尴尬,那这时候,快速的重置密码就相当重要了.废话不多说,今天我们就一起来学习一下如何快速重置密码 ...
- mysql 去重
select *, count(distinct name) from table group by name http://blog.sina.com.cn/s/blog_7e7249c301012 ...
- AttributeUsage
[AttributeUsage] System.AttributeUsage声明一个Attribute的使用范围与使用原则. AllowMultiple 和 Inherited 参数是可选的,所以此代 ...
- OpenGL位图函数
[OpenGL位图函数] 1.OpenGL中glBitmap用于绘制一个二值阵列. When drawn, the bitmap is positioned relative to the curre ...
- 解剖Nginx·自动脚本篇(5)编译器相关主脚本
在 Nginx 的自动脚本中,auto/cc目录下的所有脚本都是用于编译器相关配置使用的.Nginx的出色跨平台性(Linux.Darwin.Solaris.Win32 等)就有这些脚本的贡献.该目录 ...
- EntityFramework - Code First - 数据迁移
需求 在更新模型之后同步更新数据库里的表,并不丢失原有数据 使用默认值填充新增加的字段 EntityFramework迁移命令 Enable-Migrations 启用迁移 Add-Migration ...
- p2408 不同子串个数
传送门 分析 首先我们不难求出一共有多少子串 之后我们只需要减掉重复个数即可 于是我们对于每个后缀减去它跟它前一名的最长公共前缀即可 代码 #include<iostream> #incl ...
- [JAVA] 小数转百分数
import java.text.NumberFormat; //获取格式化对象 NumberFormat format = NumberFormat.getPercentInstance(); // ...