《Head First 设计模式》:状态模式
正文
一、定义
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
要点:
- 状态模式允许一个对象基于内部状态而拥有不同的行为。
- 状态模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象。
- 通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了。
二、实现步骤
1、创建状态接口
/**
* 状态接口
*/
public interface State {
/**
* 根据状态进行处理的方法
*/
public void handle();
}
2、在持有状态的类中,将请求委托给状态类
/**
* 持有状态的上下文类
*/
public class Context {
private State state;
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
/**
* 接收请求,并将请求委托给状态类
*/
public void request() {
state.handle();
}
}
3、创建具体的状态,并实现状态接口
(1)具体状态A
/**
* 具体状态A
*/
public class ConcreteStateA implements State {
Context context;
public ConcreteStateA() {
context = new Context();
}
@Override
public void handle() {
// 实现该状态下相应的行为
System.out.println("Context is in A state, and start to do something...");
context.setState(this);
}
}
(2)具体状态B
/**
* 具体状态B
*/
public class ConcreteStateB implements State {
Context context;
public ConcreteStateB() {
context = new Context();
}
@Override
public void handle() {
// 实现该状态下相应的行为
System.out.println("Context is in B state, and start to do something...");
context.setState(this);
}
}
4、通过改变状态,来改变上下文类的行为
public class Test {
public static void main(String[] args) {
// 上下文
Context context = new Context();
// 状态
State stateA = new ConcreteStateA();
State stateB = new ConcreteStateB();
// 通过状态改变行为
context.setState(stateA);
context.request();
context.setState(stateB);
context.request();
}
}
三、举个栗子
1、背景
万能糖果公司打算使用 Java 来实现糖果机的控制器。他们希望设计能够尽量有弹性而且好维护,因为将来可能要为糖果机增加更多的行为。
糖果机的工作流程如下:

2、实现
(1)创建状态接口,并定义相应的糖果机行为
/**
* 状态接口
*/
public interface State {
/**
* 投入25分钱
*/
public void insertQuarter();
/**
* 退回25分钱
*/
public void ejectQuarter();
/**
* 转动曲柄
*/
public void turnCrank();
/**
* 发放糖果
*/
public void dispense();
}
(2)创建糖果机
将传递给糖果机的请求,委托给状态类。
/**
* 糖果机
*/
public class GumballMachine {
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State state = soldOutState;
int gumballCount = 0;
public GumballMachine(int initGumballCount) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
// 初始化糖果数量
this.gumballCount = initGumballCount;
// 初始化糖果机状态
if (initGumballCount > 0) {
state = noQuarterState;
} else {
state = soldOutState;
}
}
/**
* 投入25分钱
*/
public void insertQuarter() {
state.insertQuarter();
}
/**
* 退回25分钱
*/
public void ejectQuarter() {
state.ejectQuarter();
}
/**
* 转动曲柄
*/
public void turnCrank() {
state.turnCrank();
state.dispense();
}
/**
* 发放糖果
*/
public void releaseBall() {
System.out.println("A gumball comes rolling out the slot...");
if (gumballCount > 0) {
gumballCount = gumballCount -1;
}
}
public void setState(State state) {
this.state = state;
}
public int getGumballCount() {
return gumballCount;
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getSoldState() {
return soldState;
}
}
(3)创建具体的状态,并实现状态接口
/**
* 未投入25分钱状态
*/
public class NoQuarterState implements State {
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
// 投入25分钱,并转到已投入25分钱状态
System.out.println("You inserted a quarter");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
@Override
public void ejectQuarter() {
// 当前为未投入25分钱状态,不能退回25分钱
System.out.println("You haven't inserted a quarter");
}
@Override
public void turnCrank() {
// 当前为未投入25分钱状态,不能转动曲柄
System.out.println("You truned, but there's no quarter");
}
@Override
public void dispense() {
// 当前为未投入25分钱状态,不能发放糖果
System.out.println("You need to pay first");
}
}
/**
* 已投入25分钱状态
*/
public class HasQuarterState implements State {
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
// 当前为已投入25分钱状态,不能再次投入
System.out.println("You can't insert another quarter");
}
@Override
public void ejectQuarter() {
// 退回25分钱,并将状态转到未投入25分钱状态
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
@Override
public void turnCrank() {
// 转动曲柄,并将状态转为售出状态
System.out.println("You turned...");
gumballMachine.setState(gumballMachine.getSoldState());
}
@Override
public void dispense() {
// 当前为已投入25分钱状态,还未转动曲柄,不能发放糖果
System.out.println("No gumball dispensed");
}
}
/**
* 售出状态
*/
public class SoldState implements State {
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
// 当前为售出状态,不能再次投入25分钱
System.out.println("Please wait, we're already giving you a gumball");
}
@Override
public void ejectQuarter() {
// 当前为售出状态,不能退回25分钱
System.out.println("Sorry, you already truned the crank");
}
@Override
public void turnCrank() {
// 当前为售出状态,不能再次转动曲柄
System.out.println("Turning twice doesn't get you another gumball!");
}
@Override
public void dispense() {
// 发放糖果
gumballMachine.releaseBall();
if (gumballMachine.getGumballCount() > 0) {
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
System.out.println("Oops, out of gumballs!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
}
/**
* 售罄状态
*/
public class SoldOutState implements State {
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
// 当前为售罄状态,不能投入25分钱
System.out.println("You can't insert a quarter, the machine is sold out");
}
@Override
public void ejectQuarter() {
// 当前为售罄状态,不能要求退回25分钱
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
@Override
public void turnCrank() {
// 当前为售罄状态,不能转动曲柄
System.out.println("You turned, but there are no gumballs");
}
@Override
public void dispense() {
// 当前为售罄状态,不能发放糖果
System.out.println("No gumball dispensed");
}
}
(4)操作糖果机
public class Test {
public static void main(String[] args) {
// 糖果机
GumballMachine gumballMachine = new GumballMachine(5);
// 正常操作
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println("-----------------------");
// 异常操作
gumballMachine.insertQuarter();
gumballMachine.ejectQuarter();
gumballMachine.turnCrank();
}
}
《Head First 设计模式》:状态模式的更多相关文章
- <人人都懂设计模式>-状态模式
同样是水,固态,气态,液态的变化,是由温度引起. 引此为思考状态模式. from abc import ABCMeta, abstractmethod # 引入ABCMeta和abstractmeth ...
- <人人都懂设计模式>-中介模式
真正的用房屋中介来作例子, 好的书籍总是让人记忆深刻. class HouseInfo: def __init__(self, area, price, has_window, has_bathroo ...
- <人人都懂设计模式>-单例模式
这个模式,我还是了解的. 书上用了三种不同的方法. class Singleton1: # 单例实现方式1 __instance = None __is_first_init = False def ...
- <人人都懂设计模式>-装饰模式
书上,真的用一个人穿衣打拌来讲解装饰模式的呢. from abc import ABCMeta, abstractmethod class Person(metaclass=ABCMeta): def ...
- 14. 星际争霸之php设计模式--状态模式
题记==============================================================================本php设计模式专辑来源于博客(jymo ...
- 人人都懂区块链--pdf电子版学习资料下载
人人都懂区块链 21天从区块链“小白”到资深玩家电子版pdf下载 链接:https://pan.baidu.com/s/1TWxYv4TLa2UtTgU-HqLECQ 提取码:6gy0 好的学习资料需 ...
- [Head First设计模式]生活中学设计模式——状态模式
系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...
- 深入浅出设计模式——状态模式(State Pattern)
模式动机 在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的 (stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的.当一个这样的 ...
- 24种设计模式--状态模式【State Pattern】
现在城市发展很快,百万级人口的城市一堆一堆的,那其中有两个东西的发明在城市的发展中起到非常重要的作用:一个是汽车,一个呢是...,猜猜看,是什么?是电梯!汽车让城市可以横向扩展,电梯让城市可以纵向延伸 ...
- C++设计模式——状态模式
前言 在实际开发中,我们经常会遇到这种情况:一个对象有多种状态,在每一个状态下,都会有不同的行为.那么在代码中我们经常是这样实现的. typedef enum tagState { state, st ...
随机推荐
- 创建SpringMVC项目
学习SpringMVC框架第一步,先创建一个简单项目,了解流程.使用的是Eclipse+Tomcat9.0 创建项目springmvc 新建Dynamic Web Project File->N ...
- RPC 框架通俗解释 转自知乎(洪春涛)
本地过程调用 RPC就是要像调用本地的函数一样去调远程函数.在研究RPC前,我们先看看本地调用是怎么调的.假设我们要调用函数Multiply来计算lvalue * rvalue的结果: 那么在第8行时 ...
- C++ Templates 目录
第1部分 : 基本概念 第1章 函数模板 1.1 初识函数模板 1.1.1 定义模板 1.1.2 使用模板 1.1.3 二阶段翻译 1.2 模板参数推导 1.3 多模板参数 1.3.1 返回类型的模板 ...
- Linux查看系统硬件信息命令汇总
对工作中,经常用的查看系统硬件命令的一个简单总结 1.查看磁盘类型(是否SSD盘)cat /sys/block/sda/queue/rotational注意:命令中的sba是你的磁盘名称,可以通过df ...
- 使用服务进程启动asp.net core程序
昨天在PuTTY中启动了测试网站,因为时间太晚,看到效果,立马关机睡觉,刚上床又想看看手机端访问效果,结果一打开,发现nginx找不到页面,意识到应该是退出PuTTY导致进程结束,于是今天赶紧查了解决 ...
- e3mall商城的归纳总结6之redis
一.说在前面的话 前面几节我们主要对该项目的后端进行了增删改查,但是所有的数据都是存放在数据库中,这样的话数据库的压力显而易见是很大的,因此本节学习nosql的缓存,也就是redis的使用,在使用之前 ...
- 超简单集成华为HMS ML Kit文本识别SDK,一键实现账单号自动录入
前言 在之前的文章<超简单集成华为HMS Core MLKit通用卡证识别SDK,一键实现各种卡绑定>中我们给大家介绍了华为HMS ML Kit通用卡证识别技术是如何通过拍照自动识别卡 ...
- vue 在使用数组的时候,数组内部数据发生变化,视图却没有改变
data(){ return{ todos: [ {name: 'aa', age: 22}, {name: 'bb', age: 23} ] } } methods:{ changeTodos(){ ...
- 当this碰到return会发生什么
当this碰到return时 function fn(params) { this.user = 'fzy' return {} } var a = new fn console.log(a.user ...
- C++ —— 输出100以内的质数
代码如下: #include<iostream> #include<math.h> using namespace std; int main() { int i; for(i ...