Head First 设计模式 —— 12. 状态 (State) 模式
思考题
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) 模式的更多相关文章
- Java 实现状态(State)模式
		
/** * @author stone */ public class WindowState { private String stateValue; public WindowState(Stri ...
 - 状态(State)模式--设计模式
		
定义与特点 1.1 定义 状态模式允许一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它的类一样. 1.2 特点 状态模式优点: 封装了转换规则,并枚举可能的状态,它将所有与某个状 ...
 - [设计模式][c++]状态切换模式
		
转自:http://blog.csdn.net/yongh701/article/details/49154439 状态模式也是设计模式的一种,这种设计模式思想不复杂,就是实现起来的代码有点复杂.主要 ...
 - 状态(State)模式
		
状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它的 ...
 - 设计模式C++描述----16.状态(State)模式
		
一. 举例 一般汽车发动机工作时有四种状态,吸气.压缩.做功和排气. 在运行时,不同的状态会有不同的行为,当前的状态机在适当的时候会过渡到下一状态. 其实用户在使用时根本不知道当前的状态,也无需知道当 ...
 - C#设计模式(12)——享元模式(Flyweight Pattern)
		
一.引言 在软件开发过程,如果我们需要重复使用某个对象的时候,如果我们重复地使用new创建这个对象的话,这样我们在内存就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非 ...
 - 设计模式--模板方法 And State模式
		
1.模板方法 钩子: 在抽象基类已经有默认的定义,子类选择是否覆盖它 在模板方法模式中, 抽象基类中使用 钩子函数(子类视情况是否覆盖) 来达到控制模板方法中 流程控制的 目的 设计原则: ...
 - C#设计模式(12)——享元模式(Flyweight Pattern)(转)
		
一.引言 在软件开发过程,如果我们需要重复使用某个对象的时候,如果我们重复地使用new创建这个对象的话,这样我们在内存就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非 ...
 - java设计模式-----12、外观模式
		
Facade模式也叫外观模式,是由GoF提出的23种设计模式中的一种.Facade模式为一组具有类似功能的类群,比如类库,子系统等等,提供一个一致的简单的界面.这个一致的简单的界面被称作facade. ...
 
随机推荐
- 世界上最快的排序算法——Timsort
			
前言 经过60多年的发展,科学家和工程师们发明了很多排序算法,有基本的插入算法,也有相对高效的归并排序算法等,他们各有各的特点,比如归并排序性能稳定.堆排序空间消耗小等等.但是这些算法也有自己的局限性 ...
 - uni-app快速入门教程
			
1.什么是uni-app? uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS.Android.H5.以及各种小程序(微信/支付宝/百度/头条/QQ/ ...
 - MySQL全备及备份文件删除脚本
			
1.数据库全备 #!/bin/shv_user="root"v_password="mysql"backup_date=`date +%Y%m%d%H%M` M ...
 - expdp、impdp状态查看及中断方法
			
一.expdp状态查看及中断方法 1.查询expdp的job的名字 SQL> select job_name from dba_datapump_jobs; JOB_NAME---------- ...
 - 其它语言通过HiveServer2访问Hive
			
先解释一下几个名词: metadata :hive元数据,即hive定义的表名,字段名,类型,分区,用户这些数据.一般存储关系型书库mysql中,在测试阶段也可以用hive内置Derby数据库. me ...
 - PHPCMS V9.6.3的后台漏洞分析
			
PHPCMS V9.6.3后台的漏洞分析 1.利用文件包含创建任意文件getshell 漏洞文件:\phpcmsv9\phpcms\modules\block\block_admin.php 漏洞产生 ...
 - Python 学习笔记(上)
			
Python 学习笔记(上) 这份笔记是我在系统地学习python时记录的,它不能算是一份完整的参考,但里面大都是我觉得比较重要的地方. 目录 Python 学习笔记(上) 基础知识 基本输入输出 模 ...
 - ASP.NET Core 3.1 IOC容器以及默认DI以及替换Autofac生命周期
			
IOC 就是我们需要一个对象 以前我们是去 new 现在我们是直接向 IOC容器 要我们需要的那个对象. 使用一个IOC容器(autofac)通过依赖注入控制各个组件的耦合.也就是说你写好了组件,不需 ...
 - Nginx 配置日志路径(nginx.conf没有写log路径,所以debug的时候找不到日志)
			
缘由:nginx.conf没有写log路径,所以debug的时候找不到日志,遂在conf文件里写入了log路径 Setp1.nginx默认日志路径: /var/log/nginx Setp2.conf ...
 - 3.自定义view-TextView变色
			
1.效果 2.实现原理 自定义Textview,重写onDraw方法,将画布分成两部分,用不同颜色的画笔画 核心代码: @Override protected void onDraw(Canvas c ...
 
			
		