正文

一、定义

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

要点:

  • 状态模式允许一个对象基于内部状态而拥有不同的行为。
  • 状态模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象。
  • 通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了。

二、实现步骤

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 设计模式》:状态模式的更多相关文章

  1. <人人都懂设计模式>-状态模式

    同样是水,固态,气态,液态的变化,是由温度引起. 引此为思考状态模式. from abc import ABCMeta, abstractmethod # 引入ABCMeta和abstractmeth ...

  2. <人人都懂设计模式>-中介模式

    真正的用房屋中介来作例子, 好的书籍总是让人记忆深刻. class HouseInfo: def __init__(self, area, price, has_window, has_bathroo ...

  3. <人人都懂设计模式>-单例模式

    这个模式,我还是了解的. 书上用了三种不同的方法. class Singleton1: # 单例实现方式1 __instance = None __is_first_init = False def ...

  4. <人人都懂设计模式>-装饰模式

    书上,真的用一个人穿衣打拌来讲解装饰模式的呢. from abc import ABCMeta, abstractmethod class Person(metaclass=ABCMeta): def ...

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

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

  6. 人人都懂区块链--pdf电子版学习资料下载

    人人都懂区块链 21天从区块链“小白”到资深玩家电子版pdf下载 链接:https://pan.baidu.com/s/1TWxYv4TLa2UtTgU-HqLECQ 提取码:6gy0 好的学习资料需 ...

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

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

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

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

  9. 24种设计模式--状态模式【State Pattern】

    现在城市发展很快,百万级人口的城市一堆一堆的,那其中有两个东西的发明在城市的发展中起到非常重要的作用:一个是汽车,一个呢是...,猜猜看,是什么?是电梯!汽车让城市可以横向扩展,电梯让城市可以纵向延伸 ...

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

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

随机推荐

  1. gorilla/mux 的学习

    原文链接:gorilla/mux的学习 源代码: package main import ( "encoding/json" "fmt" "githu ...

  2. Redis取出中文乱码问题

    直接打开Redis客户端,如果存储了中文的内容,取出时会发生乱码: 解决办法很简单,打开客户端时加上 --raw,这样就可以了: 顺便说一下,-a后面跟的是密码,打码了,各位见谅!

  3. 仿京东BOE官网 css代码

    * { margin: 0; padding: 0; border: 0; list-style: none; } .box { width: 1518px; height: 1300px; marg ...

  4. 第4篇scrum冲刺(5.24)

    一.站立会议 1.照片 2.工作安排 成员 昨天已完成的工作 今天的工作安排 困难 陈芝敏  完成云开发配置,初始化数据库:  线下模块(还剩下获取词的数据库) 倒计时模块的初加载还是有点慢  冯晓凤 ...

  5. Springboot中登录后关于cookie和session拦截案例

    目录 一.前言 二.利用Cookie进行登录验证 一.前言 1.简单的登录验证可以通过Session或者Cookie实现. 2.每次登录的时候都要进数据库校验下账户名和密码,只是加了cookie 或s ...

  6. Ajax、XMLHttpRequest、JSONP的区别

    来自2020年搜狗的笔试题,第一题就不会

  7. .NetCore对接各大财务软件凭证API——用友系列(3)

    一. 前言 由于前段时间项目比较集中,所以停更了好久,终于来到我们用友的系列产品3---U8Cloud2.7了. 一,2.7和2.5的api方式有什么区别? 1.2.7版本以后可以直接使用u8c登入地 ...

  8. rust 模块组织结构

    rust有自己的规则和约定用来组织模块,比如一个包最多可以有一个库crate,任意多个二进制crate.导入文件夹内的模块的两种约定方式... 知道这些约定,就可以快速了解rust的模块系统. 先把一 ...

  9. Java/后端学习路线

    点赞再看,养成习惯,微信搜一搜[三太子敖丙]关注这个喜欢写情怀的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系 ...

  10. Android开发之TextView中间设置横线,适用于电商项目,商品原价之类的功能。

    textview.getPaint().setFlags(Paint. STRIKE_THRU_TEXT_FLAG ); //中间横线 textview.getPaint().setFlags(Pai ...