糖果机

  如下糖果机工作状态图,我们对这个状态图进行编码实现糖果机的工作过程

这个状态图的每个圆圈代表一个状态,可以看到有4个状态同时又4个动作,分别是:“投入1元钱”、“退回1元钱”、“转动曲柄”、“发放糖果”。当要发放糖果的时候需要判断糖果数量是否为0来进入“糖果售磐”或者“没有1元钱”状态。所以有5个状态转换。

接下来我们对状态图进行分析实现编码

①找出状态:没有1元钱、有1元钱、糖果售出、糖果售磐。

②创建实例变量持有当前状态,定义每个状态的值。

  1. static int SOLD_OUT=;
  2. static int NO_ONERMB=;
  3. static int HAS_ONERMB=;
  4. static int SOLD=;
  5.  
  6. int State=SOLD_OUT;

③将系统中的动作整合起来:投入1元、退回1元、转动曲柄、发放糖果。

  以投入1元为例

  1. public void InsertOneRMB() {
  2. if (State == HAS_ONERMB)
  3. {
  4. Console.WriteLine("已经投入了,不能再投入");
  5. }
  6. else if (State == SOLD_OUT) {
  7. Console.WriteLine("糖果已经售磐,不能再投入");
  8. }
  9. else if (State == SOLD)
  10. {
  11. Console.WriteLine("请稍后投入,正在发放糖果");
  12. }
  13. else if (State == NO_ONERMB)
  14. {
  15. State = HAS_ONERMB;
  16. Console.WriteLine("你投入了1元钱");
  17. }
  18. }

根据分析我们就可以写出糖果机的代码,其他几个动作具体实现就不再写了。

  1. class GumballMachine
  2. {
  3. readonly static int SOLD_OUT = ;
  4. readonly static int NO_ONERMB = ;
  5. readonly static int HAS_ONERMB = ;
  6. readonly static int SOLD = ;
  7.  
  8. int State = SOLD_OUT;
  9. int Count = ;
  10.  
  11. public GumballMachine(int count) {
  12. this.Count = count;
  13. if (count > )
  14. {
  15. State = NO_ONERMB;
  16. }
  17. }
  18. /// <summary>
  19. /// 投入1元
  20. /// </summary>
  21. public void InsertOneRMB() {
  22.  
  23. if (State == HAS_ONERMB)
  24. {
  25. Console.WriteLine("已经投入了,不能再投入");
  26. }
  27. else if (State == SOLD_OUT) {
  28. Console.WriteLine("糖果已经售磐,不能再投入");
  29. }
  30. else if (State == SOLD)
  31. {
  32. Console.WriteLine("请稍后投入,正在发放糖果");
  33. }
  34. else if (State == NO_ONERMB)
  35. {
  36. State = HAS_ONERMB;
  37. Console.WriteLine("你投入了1元钱");
  38. }
  39. }
  40. /// <summary>
  41. /// 退回1元
  42. /// </summary>
  43. public void EjectOneRMB() { }
  44.  
  45. /// <summary>
  46. /// 转动手柄
  47. /// </summary>
  48. public void TurnCrank() { }
  49.  
  50. /// <summary>
  51. /// 发放糖果
  52. /// </summary>
  53. public void Dispense() { }
  54. }

通过这样的实现已经是考虑的比较周详而且代码清晰。但是该来的还是回来,需求变更仍然让我们的代码面临问题。接下来我们看如何满足需求以及状态模式的使用。

需求变更

需求:当个赢家!10人有1人可以得到一颗免费糖果(当曲柄转动时,有10%的机率掉下来两颗糖果)。

针对于这个需求我们将状态添加到状态图

针对于原来的代码怎么修改呢?首先我们需要加上一个新的状态“赢家”,然后必须在每个方法中加入一个新的条件判断处理“赢家”状态,更麻烦的是TurnCrank方法需要大改造,因为必须加上检查是否赢家来决定切换到赢家状态还是售出糖果状态。如果再加入其他状态,那么代码要继续修改,而现在的代码面对变法时有几个问题。

①没有遵循开闭原则。

②状态转换被隐藏在条件语句中,不明显。

③没有把会改变的部分封装起来。

④该设计不符合面向对象。

新的设计

我们不用现在的代码,重新它以便将状态对象封装在各自的类中,然后再动作发生时委托给当前状态。

①首先,我们定义一个Sate接口。在这个接口内,糖果机的每个动作都有一个对应的方法。

②为机器中的每个状态实现状态类。这些类负责在对应的状态下进行机器的行为。

③将动作委托到状态类。

用类图来梳理设计

按照类图进行实现,首先定义接口。然后实现NoOneRMBState

  1. public class NoOneRMBState : State
  2. {
  3. GumballMachine gumballMachine;
  4. public NoOneRMBState(GumballMachine gumballMachine)
  5. {
  6. this.gumballMachine = gumballMachine;
  7. }
  8.  
  9. public void InsertOneRMB()
  10. {
  11. Console.WriteLine("你投入了1元钱");
  12. gumballMachine.SetState(gumballMachine.hasOneRMBState); //将糖果状态改到hasOneRMBState
  13. }
  14. public void EjectOneRMB()
  15. {
  16. Console.WriteLine("没有钱可退");
  17. }
  18.  
  19. public void TurnCrank()
  20. {
  21. Console.WriteLine("没有钱,不能转动");
  22. }
  23. public void Dispense()
  24. {
  25. Console.WriteLine("没有钱,不能发放糖果");
  26. }
  27. }

其他状态类是具体的业务代码就不再一一实现了,我们最后改造糖果机

  1. public class GumballMachine
  2. {
  3. public State soldOutState { get; }
  4. public State noOneRMBState { get; }
  5. public State hasOneRMBState { get; }
  6. public State soldState { get; }
  7.  
  8. State State;
  9. int Count = ;
  10.  
  11. public GumballMachine(int count)
  12. {
  13. this.Count = count;
  14. soldOutState = new SoldOutState(this);
  15. noOneRMBState = new NoOneRMBState(this);
  16. hasOneRMBState = new HasOneRMBState(this);
  17. soldState = new SoldState(this);
  18. if (count > )
  19. {
  20. State = noOneRMBState;
  21. }
  22. else {
  23. State = soldOutState;
  24. }
  25. }
  26. /// <summary>
  27. /// 投入1元
  28. /// </summary>
  29. public void InsertOneRMB()
  30. {
  31. State.InsertOneRMB();
  32. }
  33. /// <summary>
  34. /// 退回1元
  35. /// </summary>
  36. public void EjectOneRMB() {
  37. State.EjectOneRMB();
  38. }
  39.  
  40. /// <summary>
  41. /// 转动手柄
  42. /// </summary>
  43. public void TurnCrank() {
  44. State.TurnCrank();
  45. //状态内部动作,所以我们不在需要单独一个发放糖果的方法。
  46. State.Dispense();
  47. }
  48.  
  49. /// <summary>
  50. /// 设置状态
  51. /// </summary>
  52. /// <param name="state"></param>
  53. public void SetState(State state)
  54. {
  55. this.State = state;
  56. }
  57. }

如上就是利用状态模式改造后的代码。

状态模式定义

状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像是修改了它的类。

定义的第一部分描述这个模式将状态封装为独立的类,并将动作委托到代表当前状态的对象,行为会随着内部状态而改变。例如在noOneRMBState和hasOneRMBState两个状态时,投入1元,就会得到不同的行为。

第二部分“对象看起来好像是修改了它的类”,从客户来看如果说使用的对象能够完全改变自己的行为,那么会觉得这个对象实际上是从别的类再实例化而来的。事实上我们实在使用组合简单引用不同状态对象来造成类改变的假象。

策略模式与状态模式

我们发现策略模式与状态模式类图一样,但是他们所要干事情的意图完全不一样,所以我做个简要的区分

状态模式:对象创建后,可以告诉客户从什么状态开始,然后随着时间推移改变自己的状态,而任何状态的改变都是定义好的。

策略模式:允许对象通过组合和委托来拥有不同的算法或行为。能实例化一个类,给它一个实现某些行为的策略对象,也可以在运行时改变行为。

Head First设计模式——状态模式的更多相关文章

  1. 14. 星际争霸之php设计模式--状态模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  2. [Head First设计模式]生活中学设计模式——状态模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  3. JAVA 设计模式 状态模式

    用途 状态模式 (State) 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式是一种行为型模式. 结构

  4. 深入浅出设计模式——状态模式(State Pattern)

    模式动机 在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的 (stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的.当一个这样的 ...

  5. C++设计模式——状态模式

    前言 在实际开发中,我们经常会遇到这种情况:一个对象有多种状态,在每一个状态下,都会有不同的行为.那么在代码中我们经常是这样实现的. typedef enum tagState { state, st ...

  6. C#设计模式--状态模式

    设计模式: 状态模式(State Pattern) 简单介绍: 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式. 在状态模式中,我们创建表示 ...

  7. Java设计模式—状态模式

    状态模式又是一个比较难的设计模式 定义如下: 当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类. 个人理解:通俗的讲,状态模式就是状态的改变引起了行为的改变,但是,我们只能看到行为的 ...

  8. 设计模式-状态模式(State Pattern)

    本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 状态模式简介 状态模式允许一个对象在其内部状态改变的时候改变它的行为,他的内部会存着好几种状态, ...

  9. JavaScript设计模式——状态模式

    状态和行为: 所谓对象的状态,通常指的就是对象实例的属性的值:而行为指的就是对象的功能,再具体点说,行为大多可以对应到方法上. 状态模式的功能就是分离状态的行为,通过维护状态的变化,来调用不同状态对应 ...

  10. C#设计模式——状态模式(State Pattern)

    一.概述在面向对象软件设计时,常常碰到某一个对象由于状态的不同而有不同的行为.如果用if else或是switch case等方法处理,对象操作及对象的状态就耦合在一起,碰到复杂的情况就会造成代码结构 ...

随机推荐

  1. C# 字符串与二进制的相互转换

    /// <summary> /// 将字符串转成二进制 /// </summary> /// <param name="s"></para ...

  2. Spring 资源注入

    Spring开发中经常需要调用各种资源,包含普通文件.网址.配置文件.系统环境变量等,我们可以使用Spring表达式语言(Spring-EL)实现资源的注入. Spring主要使用@Value注解实现 ...

  3. java线程相关基本方法

    java线程中常用的基本方法有wait,notify,notifyAll,sleep,join,yield等. 线程的生命周期一共分为五个部分,分别是:新建(New).就绪(Runnable).运行( ...

  4. 2013 ACM/ICPC Asia Regional Online —— Warmup2 ABEGKL

    HDU4716 A. A Computer Graphics Problem A题目描述 题意:输出手机剩余电量,保证给出的数是10的倍数. 题解:水题,按题意输出即可. 代码: #include & ...

  5. JS怎样做四舍五入

    1 .tofixed方法 toFixed() 方法可把 Number 四舍五入为指定小数位数的数字.例如将数据Num保留2位小数,则表示为:toFixed(Num):但是其四舍五入的规则与数学中的规则 ...

  6. python之对象回收机制

    python中,当程序执行完毕之后,python的垃圾回收机制就会将所有对象回收,清除占用的内存 请看如下代码 class Parent(): def __init__(self,name): sel ...

  7. Scala与Mongodb实践3-----运算环境的搭建

    目的:使的在IDEA中编辑代码,令代码实现mongodb运算,且转换较为便捷 由实验2可知,运算环境的搭建亦需要对数据进行存储和计算,故需要实现类型转换,所以在实验2的基础上搭建环境. 由菜鸟教程可得 ...

  8. Scala实践10

    1.模式匹配 模式匹配是一种根据模式检查值的机制.它是switch(Java中语句)的更强大版本,它同样可以用来代替一系列if / else语句. 句法 匹配表达式具有值,match关键字和至少一个c ...

  9. python打印图形

    i = 0 while i < 5: # print('*****') 效果与下行相同 print('*'*5) i+=1 print('\n\n') i = 1 while i < 6: ...

  10. cogs 14. [网络流24题] 搭配飞行员 二分图最大匹配 匈牙利算法

    14. [网络流24题] 搭配飞行员 ★★   输入文件:flyer.in   输出文件:flyer.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述]     飞行大队有 ...