正文

一、定义

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

要点:

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

二、实现步骤

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. Go语言 | CSP并发模型与Goroutine的基本使用

    今天是golang专题的第13篇文章,我们一起来聊聊golang当中的并发与Goroutine. 在之前的文章当中我们介绍完了golang当中常用的使用方法和规范,在接下来的文章当中和大家聊聊gola ...

  2. NameNode和SecondaryNameNode(面试开发重点)

    NameNode和SecondaryNameNode(面试开发重点) 1 NN和2NN工作机制 思考:NameNode中的元数据是存储在哪里的? 首先,我们做个假设,如果存储在NameNode节点的磁 ...

  3. maatwebsite lost precision when export long integer data

    Maatwebsite would lost precision when export long integer data, no matter string or int storaged in ...

  4. 【转】Python进度条tqdm的使用

    有时候在使用Python处理比较耗时操作的时候,为了便于观察处理进度,这时候就需要通过进度条将处理情况进行可视化展示,以便我们能够及时了解情况.这对于第三方库非常丰富的Python来说,想要实现这一功 ...

  5. Bellman-Ford算法 例题:P3371 单源最短路径

    看到还没人用Bellman-Ford过,赶紧水一发 lz非常弱,求各位大佬轻喷qwq 洛谷题目传送门:P3371 0."松弛"操作 如果存在一条边\((u,v)\)通过中继的方式可 ...

  6. zero:如何找到SEO流量的突破口

    http://www.wocaoseo.com/thread-326-1-1.html 之前一篇文章已有提到过,SEO可以分为两个阶段: 一个阶段是了解点SEO知识,然后就到网站上去找页面,看哪些招式 ...

  7. CCAI观后

    暑期和大老板的学生一起学了一暑假的计算机视觉和机器学习,以前只是在京畿范围的学校听到的报告和这阵的学习数量级都不同.当时看到了很多人的报告,忽然发现了做报告应该做的准备实际还有很多. 首先是要有充分的 ...

  8. 要不是真的喜欢学技术,谁会来用Python爬小姐姐啊

    养成习惯,先赞后看!!!不用于任何商业价值,只是自己娱乐.否则 爬虫爬的好,牢饭吃到饱.这是我们这次爬取的网址:https://www.vmgirls.com/ 很多人学习python,不知道从何学起 ...

  9. 递推dp数位

    1-n里有多少个1 #include <cstdio> #include <iostream> using namespace std; int main() { int n= ...

  10. github travis-ci持续部署hexo博客

    引言 目前我的博客源码是在coding上的,因为有很方便的持续部署,但是coding目前还不提供push文件的开放API. 因为最近做了一个一键分发平台,将博客分发到简书.CSDN等等的平台,但是我的 ...