思考题

public class GumballMachine {
final static int SOLD_OUT = 0;
final static int NO_QUARTER = 1;
final static int HAS_QUARTER = 2;
final static SOLD = 3; int state = SOLD_OUT;
int count = 0; public GumballMachine(int count) {
this.count = count;
if(count > 0) {
state = NO_QUARTER;
}
} public void insertQuarter() {
if(state == HAS_QUARTER) {
// print error message
} else if(state == NO_QUARTER) {
state = HAS_QUARTER;
// print success message
} else if(state == SOLD_OUT) {
// print error message
} else if(state == SOLD) {
// print error message
}
} public void ejectQuarter() {
// ...
} public void turnCrank() {
// ...
} public void dispense() {
// ...
}
}

下列哪一项描述了我们实现的状态?(多选) P396

  • [x] A. 这份代码确实没有遵守开放-关闭原则

    • 当新增状态时,必须在所有方法中加上对新状态的条件判断,所以没有遵守开放-关闭原则
  • [ ] B. 这份代码会让 Fortran 程序员感到骄傲
    • 不知道为什么
    • 【答案有此选项】
  • [x] C. 这个设计其实不符合面向对象
    • 这个设计是面向过程的,所有的操作都通过条件判断,没有封装状态
  • [x] D. 状态转换被埋藏在条件语句中,所以并不明显
    • 状态转换是在行为方法内的条件语句中,要找到状态转换前后的状态需要阅读行为方法内的全部代码,难以快速了解某种状态会如何转换
  • [x] E. 我们还没有把会改变的那部分包起来
    • 状态和行为都会改变,但行为比较固定且与实际相对应,状态是抽象出来的,所以应该将状态封装起来
  • [x] F. 未来加入的代码很有可能会导致 bug
    • 由于所有行为方法内都有不同状态的条件判断,所以在任何状态发生变化时,都要对所有行为方法进行修改进行处理,很容易遗忘对某行为方法的修改

思考题

public class GumballMachine {
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState; State state = soldOutState;
int count = 0; public GumballMachine(int numberGumballs) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
this.count = numberGumballs;
if (numberGumballs > 0) {
state = noQuarterState;
}
} public void insertQuarter() {
state.insertQuarter();
} public void ejectQuarter() {
state.ejectQuarter();
} public void turnCrank() {
state.turnCrank();
state.dispense();
} void setState(State state) {
this.state = state;
} void releaseBall() {
// print success message
if(count != 0) {
count = count - 1;
}
}
}

让我们来回头看看糖果机的实现。如果曲柄被转动了,但是没有成功(比方说顾客没有先投入25分钱的硬币)。在这种情况下,尽管没有必要,但我们还是会调用 dispense() 方法。对于这个问题你要如何修改呢? P405

  • State 接口的 turnCrank() 方法增加返回值以表示是否正确处理,只有在正确处理时,才调用 dispense() 方法

状态模式

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

特点

  • 将每个状态行为局部化到自己的状态类中 P407
  • 让每个状态“对修改更换比”,让上下文“对扩展开放”,因为可以加入新的状态类 P407

缺点

  • 通常会导致设计中类的数目大量增加 P423

状态模式和策略模式的区别

状态模式

  • 将一群行为封装在状态对象中,上下文的行为随时可委托到那些状态对象中的一个。当前状态会在状态对象集合中游走改变,以反应出上下文内部的状态,因此,上下文的行为也会跟着改变。但时客户对于上下文的状态对象了解不多,甚至根本是浑然不知 P411
  • 是不用在上下文中放置许多条件判断的替代方案,通过将行为包装进状态对象中,在上下文内简单地改变状态对象来改变上下文的行为 P411

策略模式

  • 客户通常主动指定上下文所要组合的策略对象 P411
  • 是除继承之外的一种弹性替代方案,可以通过组合不同的对象来改变行为 P411

思考题

应该由状态类还是上下文决定状态转换的流向? P412

  • 当状态转换是固定的时候,适合放在上下文中(此时状态类之间不相互依赖,是对状态类修改封闭) P412
  • 当状态转换是更动态的时候,通常就会放在状态类中(此时状态类之间产生了依赖,是对上下文修改封闭) P412

思考题

我们需要你为糖果机写一个重填糖果的 refill() 方法。这个方法需要一个变量——所要填入机器中的糖果数目。它应该能更新糖果机内的糖果数目,并重设机器的状态。 P421

void refill(int num) {
this.count += num;
if(state instanceof SoldOutState) {
state = noQuarterState;
}
}

思考题

配对下列模式和描述: P422

状态模式:封装基于状态的行为,并将行为委托到当前状态

策略模式:将可以互换的行为封装起来,然后使用委托的方法,决定使用哪一个行为

模板方法模式:由子类决定如何实现算法中的某些步骤

本文首发于公众号:满赋诸机(点击查看原文) 开源在 GitHub :reading-notes/head-first-design-patterns

Head First 设计模式 —— 12. 状态 (State) 模式的更多相关文章

  1. Java 实现状态(State)模式

    /** * @author stone */ public class WindowState { private String stateValue; public WindowState(Stri ...

  2. 状态(State)模式--设计模式

    定义与特点 1.1 定义 状态模式允许一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它的类一样. 1.2 特点 状态模式优点: 封装了转换规则,并枚举可能的状态,它将所有与某个状 ...

  3. [设计模式][c++]状态切换模式

    转自:http://blog.csdn.net/yongh701/article/details/49154439 状态模式也是设计模式的一种,这种设计模式思想不复杂,就是实现起来的代码有点复杂.主要 ...

  4. 状态(State)模式

    状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它的 ...

  5. 设计模式C++描述----16.状态(State)模式

    一. 举例 一般汽车发动机工作时有四种状态,吸气.压缩.做功和排气. 在运行时,不同的状态会有不同的行为,当前的状态机在适当的时候会过渡到下一状态. 其实用户在使用时根本不知道当前的状态,也无需知道当 ...

  6. C#设计模式(12)——享元模式(Flyweight Pattern)

    一.引言 在软件开发过程,如果我们需要重复使用某个对象的时候,如果我们重复地使用new创建这个对象的话,这样我们在内存就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非 ...

  7. 设计模式--模板方法 And State模式

    1.模板方法   钩子: 在抽象基类已经有默认的定义,子类选择是否覆盖它 在模板方法模式中,  抽象基类中使用 钩子函数(子类视情况是否覆盖)  来达到控制模板方法中  流程控制的 目的 设计原则: ...

  8. C#设计模式(12)——享元模式(Flyweight Pattern)(转)

    一.引言 在软件开发过程,如果我们需要重复使用某个对象的时候,如果我们重复地使用new创建这个对象的话,这样我们在内存就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非 ...

  9. java设计模式-----12、外观模式

    Facade模式也叫外观模式,是由GoF提出的23种设计模式中的一种.Facade模式为一组具有类似功能的类群,比如类库,子系统等等,提供一个一致的简单的界面.这个一致的简单的界面被称作facade. ...

随机推荐

  1. jwt与springcloud联用

    图片来源与博客:https://www.jianshu.com/p/99a458c62aa4 分布式权限管理参考:https://blog.csdn.net/qq_36236890/article/d ...

  2. 珍藏的C语言编程系列教程

    本文有332个文字,大小约为2KB,预计阅读时间1分钟 这是本人珍藏的C语言.C++系列教程. 相信每个Coder的第一门编程语言就是C语言吧, 现在也依然很热门,不谈了.直接上链接,感兴趣的直接存, ...

  3. 庐山真面目之八微服务架构 NetCore 基于 Dockerfile 文件部署

    庐山真面目之八微服务架构 NetCore 基于 Dockerfile 文件部署 一.简介      从今天开始,不出意外的话,以后所写的文章中所介绍项目的部署环境都应该会迁移到Linux环境上,而且是 ...

  4. css 09-CSS案例讲解:博雅互动

    09-CSS案例讲解:博雅互动 #前言 CSS已经学了一些基础内容了,我们来讲解一个小案例吧.以博雅互动的官网首页举例. #版心 首页的版心如下: 这里我们要普及一个概念,叫"版心" ...

  5. 多任务-python实现-迭代器相关(2.1.12)

    @ 目录 1.需求 2.斐波那契数列演示 3.并不是只有for循环能接收可迭代数据类型,list,tuple也可以 1.需求 类比 早上起来吃包子 1.买1年的包子,放在冰箱,每天拿一个 2.每天下楼 ...

  6. Docker来搭建分布式文件系统FastDfs

    对于文件存储来说,一般情况下简单的处理就是在Django配置文件中配置存储目录,按照规则对文件进行上传或者下载. 实际上,当文件较少的时候,Django是可以应付的过来的.但当文件以海量形式出现的时候 ...

  7. Redis集群的分布式部署

    3.2.2:Redis Cluster: Redis  分布式部署方案: 1)  客户端分区:由客户端程序决定 key 写分配和写入的 redis node,但是需要客户端自己处理写入 分配.高可用管 ...

  8. Git常用命令大全,迅速提升你的Git水平

    原博文 https://mp.weixin.qq.com/s/hYjGyIdLK3UCEVF0lRYRCg 示例 初始化本地git仓库(创建新仓库) git init                 ...

  9. 【红日安全-VulnStack】ATT&CK实战系列——红队实战(二)

    一.环境搭建 1.1 靶场下载 靶场下载地址:http://vulnstack.qiyuanxuetang.net/vuln/detail/3/ 靶机通用密码:  1qaz@WSX 1.2 环境配置 ...

  10. Java获取X509证书里的指纹(SHA-1)从pxf文件里面

    直接通过流去获取pxf后缀文件的内容,指纹通过X509才能获取.String keyStorefile = "pfx文件地址";String strPassword = " ...