0

0

状态模式

聖光之護

聖光之護

发布时间:2024-12-08 20:21:14

|

654人浏览过

|

来源于dev.to

转载

什么是状态模式?

状态模式是一种行为模式,允许对象在其内部状态发生变化时改变其行为。该对象似乎会更改其类别。

什么时候使用它?

  • 当您的对象的行为根据其当前状态而有所不同时,请使用状态模式。
  • 当您想要实现表示有限自动机的系统时,请使用状态模式。

问题

我们正在开发自动售货机系统。这是显示自动售货机工作流程的图表。

状态模式

如果你学过计算机科学,你可能会注意到它看起来像有限自动机(如果你不知道什么是 fa,不用担心,你不需要知道它来理解状态模式)。图中,圆圈代表状态,箭头代表状态交易

无论如何,让我们开始编写我们的第一个版本的系统。

public class vendingmachine {

    // vending machine has four states
    final static int out_of_stock = 0;
    final static int idle = 1;
    final static int has_money = 2;
    final static int drink_sold = 3;

    // when vending machine is placed, it has no drink stocks
    int state = out_of_stock; // our initial state
    int stocks = 0;

    public vendingmachine(int stocks) {
        this.stocks = stocks;
        if (stocks > 0) {
            state = idle;
        }
    }

    public void insertmoney() {
        if (state == out_of_stock) {
            system.out.println("error: drink is out of stock");
        } else if (state == idle) {
            system.out.println("success: you inserted money");
            state = has_money;
        } else if (state == has_money) {
            system.out.println("error: you already inserted money, you can buy now");
        } else if (state == drink_sold) {
            system.out.println("error: please wait, we're dispensing you a drink that you bought");
        }
    }

    public void pressbuybutton() {
        // if statements for each states
    }

    public void presscancelbutton() {
        // if statements for each states
    }

    public void dispensedrink() {
        // if statements for each states
    }

    public void refill() {
        // if statements for each states
    }
}

你能发现问题吗?

  • 这段代码违反了开闭原则。
  • 此代码违反了单一责任原则。
  • 此代码没有封装变化的内容。
  • 状态转换并不显式,它们被放置在条件语句下。
  • 如果添加其他状态或状态转换,可能会导致已经工作的代码出现错误。

解决方案

那么,我们如何解决这些问题呢?我们将把每个状态变成一个对象,然后实现表示从该特定状态(对象)进行状态转换的方法。

状态模式

Melodrive
Melodrive

Melodrive -一个AI音乐引擎,根据用户的情绪状态和喜好生成个性化的音乐。

下载
  1. 客户
    客户端与自动售货机交互。

  2. 自动售货机
    这个类拥有多个状态。通过引入state接口,依赖于接口而不是实现。

  3. 状态
    为所有concretestate提供通用接口。

  4. 具体状态
    每个状态事务根据状态有不同的行为。例如,idle 类中定义的 insertmoney 方法和 outofstock 状态下的方法的行为不同。

现在,vendingmachine 中的许多 if 语句被删除,并且状态转换是显式的(您将在实现中看到这一点)。

结构

状态模式

这个结构让你想起另一种模式吗?是的,这个类图基本上与策略模式相同,但它们的意图不同。我们稍后再讨论。

java实现

// declare all possible state transition methods
public interface state {
    void insertmoney();
    void pressbuybutton();
    void presscancelbutton();
    void dispensedrink();
    void refill(int stocks);
}
public class drinksold implements state {

    vendingmachine machine;

    public drinksold(vendingmachine machine) {
        this.machine = machine;
    }

    @override
    public void insertmoney() {
        system.out.println("error: no state transition occurs");
    }

    @override
    public void pressbuybutton() {
        system.out.println("error: no state transition occurs");
    }

    @override
    public void presscancelbutton() {
        system.out.println("error: no state transition occurs");
    }

    @override
    public void dispensedrink() {
        machine.customertakesdrink();
        if (machine.getstocks() > 0) {
            system.out.println("success: drinksold -> idle");
            machine.setstate(machine.getidle());
        } else {
            system.out.println("success: drinksold -> outofstock");
            machine.setstate(machine.getoutofstock());
        }
    }

    @override
    public void refill(int stocks) {
        system.out.println("error: no state transition occurs");
    }
}

我将跳过其他具体状态,因为它们很相似,但您可以在我的 github 存储库中查看它们(我的存储库的链接位于本博客末尾)。

public class vendingmachine {

    private state idle;
    private state hasmoney;
    private state drinksold;
    private state outofstock;

    private state currentstate;
    int stocks = 0;

    public vendingmachine(int stocks) {
        idle = new idle(this);
        hasmoney = new hasmoney(this);
        drinksold = new drinksold(this);
        outofstock = new outofstock(this);

        this.stocks = stocks;
        if (stocks > 0) {
            currentstate = idle;
        } else {
            currentstate = outofstock;
        }
    }

    // state transition methods, actual implementation is delegated to concrete states
    public void insertmoney() {
        currentstate.insertmoney();
    }

    public void pressbuybutton() {
        currentstate.pressbuybutton();
        currentstate.dispensedrink();
    }

    public void presscancelbutton() {
        currentstate.presscancelbutton();
    }

    public void refill(int stocks) {
        currentstate.refill(stocks);
    }

    // method to be used by concrete states to move one state to another
    public void setstate(state state) {
        currentstate = state;
    }

    // helper method used when dispensing a drink on drinksold state
    public void customertakesdrink() {
        if (stocks > 0) {
            system.out.println("customer grab a drink");
            stocks--;
        }
    }

    // getter for each state
    public state getidle() {
        return idle;
    }

    public state gethasmoney() {
        return hasmoney;
    }

    public state getdrinksold() {
        return drinksold;
    }

    public state getoutofstock() {
        return outofstock;
    }

    public int getstocks() {
        return stocks;
    }

    public void setstocks(int stocks) {
        this.stocks = stocks;
    }

    @override
    public string tostring() {
        return "vendingmachine {" +
                "currentstate: " + currentstate.getclass().getsimplename() +
                ", stocks: " + stocks +
                '}';
    }
}
public class vendingmachinetestdrive {

    public static void main(string[] args) {
        vendingmachine machine = new vendingmachine(2);

        system.out.println(machine);

        system.out.println("-- customer insert money and cancel the transaction --");
        machine.insertmoney();
        machine.presscancelbutton();

        system.out.println("-- customer insert money and buy a drink --");
        machine.insertmoney();
        machine.pressbuybutton();

        system.out.println(machine);

        system.out.println("-- customer insert money and buy a drink --");
        machine.insertmoney();
        machine.pressbuybutton();

        system.out.println(machine);

        system.out.println("-- customer insert money --");
        machine.insertmoney();

        system.out.println("-- owner is going to refill drinks --");
        machine.refill(2);
        system.out.println(machine);
    }
}

输出:

VendingMachine {currentState: Idle, stocks: 2}
-- Customer insert money and cancel the transaction --
SUCCESS: Idle -> HasMoney
SUCCESS: HasMoney -> Idle
-- Customer insert money and buy a drink --
SUCCESS: Idle -> HasMoney
SUCCESS: HasMoney -> DrinkSold
Customer grab a drink
SUCCESS: DrinkSold -> Idle
VendingMachine {currentState: Idle, stocks: 1}
-- Customer insert money and buy a drink --
SUCCESS: Idle -> HasMoney
SUCCESS: HasMoney -> DrinkSold
Customer grab a drink
SUCCESS: DrinkSold -> OutOfStock
VendingMachine {currentState: OutOfStock, stocks: 0}
-- Customer insert money --
ERROR: no state transition occurs
-- Owner is going to refill drinks --
SUCCESS: OutOfStock -> Idle
VendingMachine {currentState: Idle, stocks: 2}

陷阱

  • 状态模式最终往往会拥有很多类,因为存在的类就会有尽可能多的类。如果某些状态几乎相同,您可以将它们组合在一起。这样,我们减少了重复代码,但违反了单一责任原则,请记住,当您使用状态模式时,它们是权衡的。
  • 如果一个状态有多个可达状态,实现起来会比较复杂。

与策略模式的比较

  • 它们都允许对象通过组合和委托来合并不同的行为。
  • 在策略模式中,“客户端”在运行时更改对象的行为。另一方面,在状态模式中,“上下文”根据明确定义的状态转换方法来改变状态。

您可以在这里查看所有设计模式的实现。
github 存储库

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

846

2023.08.22

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1923

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

656

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2392

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

47

2026.01.19

github中文官网入口 github中文版官网网页进入
github中文官网入口 github中文版官网网页进入

github中文官网入口https://docs.github.com/zh/get-started,GitHub 是一种基于云的平台,可在其中存储、共享并与他人一起编写代码。 通过将代码存储在GitHub 上的“存储库”中,你可以: “展示或共享”你的工作。 持续“跟踪和管理”对代码的更改。

4122

2026.01.21

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

22

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

48

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

93

2026.03.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Git 教程
Git 教程

共21课时 | 4.1万人学习

Git版本控制工具
Git版本控制工具

共8课时 | 1.6万人学习

Git中文开发手册
Git中文开发手册

共0课时 | 94人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号