Head First设计模式——状态模式
糖果机
如下糖果机工作状态图,我们对这个状态图进行编码实现糖果机的工作过程
这个状态图的每个圆圈代表一个状态,可以看到有4个状态同时又4个动作,分别是:“投入1元钱”、“退回1元钱”、“转动曲柄”、“发放糖果”。当要发放糖果的时候需要判断糖果数量是否为0来进入“糖果售磐”或者“没有1元钱”状态。所以有5个状态转换。
接下来我们对状态图进行分析实现编码
①找出状态:没有1元钱、有1元钱、糖果售出、糖果售磐。
②创建实例变量持有当前状态,定义每个状态的值。
static int SOLD_OUT=;
static int NO_ONERMB=;
static int HAS_ONERMB=;
static int SOLD=; int State=SOLD_OUT;
③将系统中的动作整合起来:投入1元、退回1元、转动曲柄、发放糖果。
以投入1元为例
public void InsertOneRMB() {
if (State == HAS_ONERMB)
{
Console.WriteLine("已经投入了,不能再投入");
}
else if (State == SOLD_OUT) {
Console.WriteLine("糖果已经售磐,不能再投入");
}
else if (State == SOLD)
{
Console.WriteLine("请稍后投入,正在发放糖果");
}
else if (State == NO_ONERMB)
{
State = HAS_ONERMB;
Console.WriteLine("你投入了1元钱");
}
}
根据分析我们就可以写出糖果机的代码,其他几个动作具体实现就不再写了。
class GumballMachine
{
readonly static int SOLD_OUT = ;
readonly static int NO_ONERMB = ;
readonly static int HAS_ONERMB = ;
readonly static int SOLD = ; int State = SOLD_OUT;
int Count = ; public GumballMachine(int count) {
this.Count = count;
if (count > )
{
State = NO_ONERMB;
}
}
/// <summary>
/// 投入1元
/// </summary>
public void InsertOneRMB() { if (State == HAS_ONERMB)
{
Console.WriteLine("已经投入了,不能再投入");
}
else if (State == SOLD_OUT) {
Console.WriteLine("糖果已经售磐,不能再投入");
}
else if (State == SOLD)
{
Console.WriteLine("请稍后投入,正在发放糖果");
}
else if (State == NO_ONERMB)
{
State = HAS_ONERMB;
Console.WriteLine("你投入了1元钱");
}
}
/// <summary>
/// 退回1元
/// </summary>
public void EjectOneRMB() { } /// <summary>
/// 转动手柄
/// </summary>
public void TurnCrank() { } /// <summary>
/// 发放糖果
/// </summary>
public void Dispense() { }
}
通过这样的实现已经是考虑的比较周详而且代码清晰。但是该来的还是回来,需求变更仍然让我们的代码面临问题。接下来我们看如何满足需求以及状态模式的使用。
需求变更
需求:当个赢家!10人有1人可以得到一颗免费糖果(当曲柄转动时,有10%的机率掉下来两颗糖果)。
针对于这个需求我们将状态添加到状态图
针对于原来的代码怎么修改呢?首先我们需要加上一个新的状态“赢家”,然后必须在每个方法中加入一个新的条件判断处理“赢家”状态,更麻烦的是TurnCrank方法需要大改造,因为必须加上检查是否赢家来决定切换到赢家状态还是售出糖果状态。如果再加入其他状态,那么代码要继续修改,而现在的代码面对变法时有几个问题。
①没有遵循开闭原则。
②状态转换被隐藏在条件语句中,不明显。
③没有把会改变的部分封装起来。
④该设计不符合面向对象。
新的设计
我们不用现在的代码,重新它以便将状态对象封装在各自的类中,然后再动作发生时委托给当前状态。
①首先,我们定义一个Sate接口。在这个接口内,糖果机的每个动作都有一个对应的方法。
②为机器中的每个状态实现状态类。这些类负责在对应的状态下进行机器的行为。
③将动作委托到状态类。
用类图来梳理设计
按照类图进行实现,首先定义接口。然后实现NoOneRMBState
public class NoOneRMBState : State
{
GumballMachine gumballMachine;
public NoOneRMBState(GumballMachine gumballMachine)
{
this.gumballMachine = gumballMachine;
} public void InsertOneRMB()
{
Console.WriteLine("你投入了1元钱");
gumballMachine.SetState(gumballMachine.hasOneRMBState); //将糖果状态改到hasOneRMBState
}
public void EjectOneRMB()
{
Console.WriteLine("没有钱可退");
} public void TurnCrank()
{
Console.WriteLine("没有钱,不能转动");
}
public void Dispense()
{
Console.WriteLine("没有钱,不能发放糖果");
}
}
其他状态类是具体的业务代码就不再一一实现了,我们最后改造糖果机
public class GumballMachine
{
public State soldOutState { get; }
public State noOneRMBState { get; }
public State hasOneRMBState { get; }
public State soldState { get; } State State;
int Count = ; public GumballMachine(int count)
{
this.Count = count;
soldOutState = new SoldOutState(this);
noOneRMBState = new NoOneRMBState(this);
hasOneRMBState = new HasOneRMBState(this);
soldState = new SoldState(this);
if (count > )
{
State = noOneRMBState;
}
else {
State = soldOutState;
}
}
/// <summary>
/// 投入1元
/// </summary>
public void InsertOneRMB()
{
State.InsertOneRMB();
}
/// <summary>
/// 退回1元
/// </summary>
public void EjectOneRMB() {
State.EjectOneRMB();
} /// <summary>
/// 转动手柄
/// </summary>
public void TurnCrank() {
State.TurnCrank();
//状态内部动作,所以我们不在需要单独一个发放糖果的方法。
State.Dispense();
} /// <summary>
/// 设置状态
/// </summary>
/// <param name="state"></param>
public void SetState(State state)
{
this.State = state;
}
}
如上就是利用状态模式改造后的代码。
状态模式定义
状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像是修改了它的类。
定义的第一部分描述这个模式将状态封装为独立的类,并将动作委托到代表当前状态的对象,行为会随着内部状态而改变。例如在noOneRMBState和hasOneRMBState两个状态时,投入1元,就会得到不同的行为。
第二部分“对象看起来好像是修改了它的类”,从客户来看如果说使用的对象能够完全改变自己的行为,那么会觉得这个对象实际上是从别的类再实例化而来的。事实上我们实在使用组合简单引用不同状态对象来造成类改变的假象。
策略模式与状态模式
我们发现策略模式与状态模式类图一样,但是他们所要干事情的意图完全不一样,所以我做个简要的区分
状态模式:对象创建后,可以告诉客户从什么状态开始,然后随着时间推移改变自己的状态,而任何状态的改变都是定义好的。
策略模式:允许对象通过组合和委托来拥有不同的算法或行为。能实例化一个类,给它一个实现某些行为的策略对象,也可以在运行时改变行为。
Head First设计模式——状态模式的更多相关文章
- 14. 星际争霸之php设计模式--状态模式
题记==============================================================================本php设计模式专辑来源于博客(jymo ...
- [Head First设计模式]生活中学设计模式——状态模式
系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...
- JAVA 设计模式 状态模式
用途 状态模式 (State) 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式是一种行为型模式. 结构
- 深入浅出设计模式——状态模式(State Pattern)
模式动机 在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的 (stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的.当一个这样的 ...
- C++设计模式——状态模式
前言 在实际开发中,我们经常会遇到这种情况:一个对象有多种状态,在每一个状态下,都会有不同的行为.那么在代码中我们经常是这样实现的. typedef enum tagState { state, st ...
- C#设计模式--状态模式
设计模式: 状态模式(State Pattern) 简单介绍: 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式. 在状态模式中,我们创建表示 ...
- Java设计模式—状态模式
状态模式又是一个比较难的设计模式 定义如下: 当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类. 个人理解:通俗的讲,状态模式就是状态的改变引起了行为的改变,但是,我们只能看到行为的 ...
- 设计模式-状态模式(State Pattern)
本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 状态模式简介 状态模式允许一个对象在其内部状态改变的时候改变它的行为,他的内部会存着好几种状态, ...
- JavaScript设计模式——状态模式
状态和行为: 所谓对象的状态,通常指的就是对象实例的属性的值:而行为指的就是对象的功能,再具体点说,行为大多可以对应到方法上. 状态模式的功能就是分离状态的行为,通过维护状态的变化,来调用不同状态对应 ...
- C#设计模式——状态模式(State Pattern)
一.概述在面向对象软件设计时,常常碰到某一个对象由于状态的不同而有不同的行为.如果用if else或是switch case等方法处理,对象操作及对象的状态就耦合在一起,碰到复杂的情况就会造成代码结构 ...
随机推荐
- 洛谷$P3647\ [APIO2014]$连珠线 换根$dp$
正解:换根$dp$ 解题报告: 传送门! 谁能想到$9102$年了$gql$居然还没写过换根$dp$呢,,,$/kel$ 考虑固定了从哪个点开始之后,以这个点作为根,蓝线只可能是直上直下的,形如&qu ...
- $loj$10222 佳佳的$Fibonacci$ 矩阵快速幂
正解:矩阵快速幂 解题报告: 我永远喜欢loj! 一看到这个就应该能想到矩阵快速幂? 然后就考虑转移式,发现好像直接想不好想,,,主要的问题在于这个*$i$,就很不好搞$QAQ$ 其实不难想到,$\s ...
- 前端页面表格排序 jQuery Table 基础
通常来说, 排序的方式有两种, 一种是我们在查询的时候就排好序,然后将数据渲染到前台页面上, 但是这样做有个弊端,就是在争对做好了缓存处理的系统, 在查询相同数据的时候进行排序,可能不能成功, 因为进 ...
- hexo博客零基础搭建系列(一)
文章目录 其他搭建 1.简介 2.安装Node和Git 3.安装Hexo 4.Hexo的目录结构 5.我的版本 其他搭建 不好意思,下面的链接都是CSDN的链接,如果要在博客园看,请点我的分类查看.因 ...
- [Windows] 系统清理与优化神器Advanced SystemCare 13 PRO非破解附正版激活码
Advanced SystemCare是一款功能强大的系统清理优化软件,该软件提供的主要功能有:启动项优化.注册表整理和清理.隐私清扫.垃圾文件清理.快捷方式修复.恶意软件清除.网络加速.系统优化.安 ...
- webpack实践——DLLPlugin 和 DLLReferencePlugin的使用
DLLPlugin 和 DLLReferencePlugin的使用 DLLPlugin 和 DLLReferencePlugin 用某种方法实现了拆分 bundles,同时还大大提升了构建的速度. 1 ...
- 低秩稀疏矩阵恢复|ADM(IALM)算法
一曲新词酒一杯,去年天气旧亭台.夕阳西下几时回? 无可奈何花落去,似曾相识燕归来.小园香径独徘徊. ---<浣溪沙·一曲新词酒一杯>--晏殊 更多精彩内容请关注微信公众号 "优化 ...
- js的alert()
效果图: 图一: 图二: 图三: 代码: <script type="text/javascript"> // alert() ; 只允许一个参数,如果有多个参数只显示 ...
- NOIP2004普及组第3题 FBI树
/* 1106: NOIP2004普及组第3题 FBI树 时间限制: 1 Sec 内存限制: 128 MB 提交: 10 解决: 9 [提交] [状态] [讨论版] [命题人:外部导入] 题目描述 我 ...
- PTA - 堆栈模拟队列
设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q. 所谓用堆栈模拟队列,实际上就是通过调用堆栈的下列操作函数: int IsFull(Stack S):判断堆栈S是否已满,返回1或0: in ...