设计模式浅析(十一) ·状态模式

news2024/11/28 10:34:13

设计模式浅析(十一) ·状态模式

日常叨逼叨

java设计模式浅析,如果觉得对你有帮助,记得一键三连,谢谢各位观众老爷😁😁


状态模式

概念

状态模式

Java中的状态模式(State Pattern)是一种行为型设计模式,它允许一个对象在其内部状态改变时改变它的行为,看起来就像修改了它的类一样。状态模式的核心在于将状态与行为绑定,不同的状态对应不同的行为。

举个🌰

假设自动贩卖机只贩卖白水这一种商品,自动贩卖机的设计师为我们提供了一张状态图,希望我们设计程序供他们的机器使用,我们先来看一下

请添加图片描述

经过一番研究,我们可以看得出,上述的状态转化图,我们可以试着将其转化为代码

首先,列出所有的状态

白水售罄售出白水有1.5元没有1.5元

然后定义一些表示状态的变量

final static Integer SOLD_OUT=0;
final static Integer NO_MONEY_1_5=1;
final static Integer HAS_MONEY_1_5=2;
final static Integer SOLD=3;

将整个售卖过程整合起来,对于售卖机器而言

 public void insertMoney() {
        if (state == HAS_MONEY_1_5) {
            //...
            System.out.println("you can't insert money ");
        } else if (state == SOLD_OUT) {
            //...
            System.out.println("you can't insert money , the machine is sold out");
        } else if (state == SOLD) {
            //...
            System.out.println("please wait, we give you a bottom of water");
        } else if (state == NO_MONEY_1_5) {
            state = HAS_MONEY_1_5;
            System.out.println("you inserted money");
        }
    }

接着上面的逻辑,我们实现整个自动化的白水贩卖机

package com.jerry.statepattern;

/**
 * @version 1.0
 * @Author jerryLau
 * @Date 2024/4/7 16:08
 * @注释 自动售卖机
 */
public class VendingMachines {
    final static Integer SOLD_OUT = 0;
    final static Integer NO_MONEY_1_5 = 1;
    final static Integer HAS_MONEY_1_5 = 2;
    final static Integer SOLD = 3;

    int state = SOLD_OUT;
    int count = 0;

    public VendingMachines(int count) {
        this.count = count;
        if (count > 0) {
            state = NO_MONEY_1_5;
        }
    }

    /***
     * 投币
     */
    public void insertMoney() {
        if (state == HAS_MONEY_1_5) {
            System.out.println("you can't insert money ");
        } else if (state == SOLD_OUT) {
            System.out.println("you can't insert money , the machine is sold out");
        } else if (state == SOLD) {
            System.out.println("please wait, we give you a bottom of water");
        } else if (state == NO_MONEY_1_5) {
            state = HAS_MONEY_1_5;
            System.out.println("you inserted money");
        }
    }

    /***
     * 退币
     */
    public void ReturnMoney() {
        if (state == HAS_MONEY_1_5) {
            System.out.println("money returned");
            state = NO_MONEY_1_5;
        } else if (state == SOLD_OUT) {
            System.out.println("you can't return,you have not  insert money yet ");
        } else if (state == SOLD) {
            System.out.println("you already passed the button");
        } else if (state == NO_MONEY_1_5) {
            System.out.println("you have not  insert money");
        }
    }


    /***
     * 按钮按下
     */
    public void passButton() {
        if (state == HAS_MONEY_1_5) {
            System.out.println("you passed the button ...");
            state = SOLD;
            //发放水
            distribute();
        } else if (state == SOLD_OUT) {
            System.out.println("you passed the button,but no water sold ");
        } else if (state == SOLD) {
            System.out.println("pass button twice doesn't get another water");
        } else if (state == NO_MONEY_1_5) {
            System.out.println("you passed the button,but you should insert money first");
        }

    }

    /***
     * 分发水
     */
    public void distribute() {
        if (state == SOLD) {
            System.out.println("A bottom of water comes rolling out the slot");
            count = count - 1;
            if (count == 0) {
                System.out.println("Oops,out of water!");
                state = SOLD_OUT;
            } else {
                state = NO_MONEY_1_5;
            }
        } else if (state == NO_MONEY_1_5) {
            System.out.println("You need to pay first");
        } else if (state == SOLD_OUT) {
            System.out.println("No water distributed");
        } else if (state == HAS_MONEY_1_5) {
            System.out.println("No water distributed");
        }

    }
    //....

    @Override
    public String toString() {
        return "VendingMachines{" +
                "state=" + state +
                ", count=" + count +
                '}';
    }
}

准备测试类

package com.jerry.statepattern;

/**
 * @version 1.0
 * @Author jerryLau
 * @Date 2024/4/7 16:09
 * @注释  VendingMachines 测试
 */
public class VendingMachinesTestMain {

    public static void main(String[] args) {
        VendingMachines gumballMachine = new VendingMachines(5);

        System.out.println(gumballMachine);

        gumballMachine.insertMoney();
        gumballMachine.passButton();
        System.out.println(gumballMachine);

        gumballMachine.insertMoney();
        gumballMachine.ReturnMoney();
        gumballMachine.passButton();
        System.out.println(gumballMachine);
//
//
        gumballMachine.insertMoney();
        gumballMachine.passButton();
        gumballMachine.insertMoney();
        gumballMachine.passButton();
        gumballMachine.ReturnMoney();
        System.out.println(gumballMachine);
//
        gumballMachine.insertMoney();
        gumballMachine.insertMoney();
        gumballMachine.passButton();
        gumballMachine.insertMoney();
        gumballMachine.passButton();
        gumballMachine.insertMoney();
        gumballMachine.passButton();
        System.out.println(gumballMachine);

    }
}

运行结果

VendingMachines{state=1, count=5}
you inserted money
you passed the button …
A bottom of water comes rolling out the slot
VendingMachines{state=1, count=4}
you inserted money
money returned
you passed the button,but you should insert money first
VendingMachines{state=1, count=4}
you inserted money
you passed the button …
A bottom of water comes rolling out the slot
you inserted money
you passed the button …
A bottom of water comes rolling out the slot
you have not insert money
VendingMachines{state=1, count=2}
you inserted money
you can’t insert money
you passed the button …
A bottom of water comes rolling out the slot
you inserted money
you passed the button …
A bottom of water comes rolling out the slot
Oops,out of water!
you can’t insert money , the machine is sold out
you passed the button,but no water sold
VendingMachines{state=0, count=0}

Process finished with exit code 0

感觉好像没什么太大的问题,我们将代码提交给自动贩卖机的老板,他们再运行后觉得非常完美,但就是太过完美了,他们想玩点花的,提出了下面的需求:

当用户投币后按下按钮后,有10%的几率掉下来的是两瓶水(多送你一瓶),这将大大加大销售量

对于他们提出的需求,emmm大概想了想,在现有代码中修改的地方比较多

  • 加一个状态
  • 对于每个步骤都得进行添加新状态的判断
  • 对于按下按钮这一方法里面的内容进行大量的修改

如果采用在现在代码上进行修改,会存在以下几种问题

  • 这份代码确实没有遵守开放-关闲原则。
  • 这个设计其实不符合面向对象。
  • 状态转换被埋藏在条件语句中,所以不明显。
  • 我们还没有把会改变的那部分包装来。
  • 未来加人的代码很有可能会导致bug。

考虑到可能会出现以上的问题,我们决定做如下新的设计

我们的计划是这样的:不要维护我们现有的代码,我们重写它以便于将状态对象封装在各自的类中,然后在动作发生时委托给当前状态。

我们在这里遵照我们的设计原则,所以最后应该得到一个容易维护的设计。

我们要做的事情是:

  • 首先,我们定义一个State接口。在这个接口内,贩卖机的每个动作都有一个对应的方法。
  • 然后为机器中的每个状态实现状态类。这些类将负责在对应的状态下进行机器的行为。
  • 最后,我们要摆脱旧的条件代码,取而代之的方式是,将动作委托到状态类。

你将会看到,我们不仅遵守了设计原则,实际上我们还实现了状态模式。在重新完成代码之后我们再来了解状态模式的正式定义…

请添加图片描述

package com.jerry.statepattern.newDisign;

/**
 * @version 1.0
 * @Author jerryLau
 * @Date 2024/4/8 8:42
 * @注释 新的状态的额接口
 */
public interface State {
    /***
     * 投币
     */
    public void insertMoney();

    /***
     * 退币
     */
    public void returnMoney() ;

    /***
     * 按钮按下
     */
    public void passButton() ;

    /***
     * 分发水
     */
    public void distribute() ;
}

状态有点多,我们简单的列举一二

package com.jerry.statepattern.newDisign;

/**
 * @version 1.0
 * @Author jerryLau
 * @Date 2024/4/8 8:45
 * @注释 没有被投币的状态
 */
public class No1_5State implements  State{
    NewVendingMachines newVendingMachines;

    public No1_5State(NewVendingMachines newVendingMachines) {
        this.newVendingMachines = newVendingMachines;
    }

    @Override
    public void insertMoney() {
        System.out.println("you insert money");
        newVendingMachines.setState(newVendingMachines.getHas1_5State());
    }

    @Override
    public void returnMoney() {
        System.out.println("you aren't insert money");
    }

    @Override
    public void passButton() {
        System.out.println("you passed Button,but no water gives to you cause you aren't insert money");
    }

    @Override
    public void distribute() {
        System.out.println("you need to pay first");
    }
}

package com.jerry.statepattern.newDisign;

/**
 * @version 1.0
 * @Author jerryLau
 * @Date 2024/4/8 8:44
 * @注释 售出状态
 */
public class SoldState implements State {
    NewVendingMachines newVendingMachines;

    public SoldState(NewVendingMachines newVendingMachines) {
        this.newVendingMachines = newVendingMachines;
    }

    @Override
    public void insertMoney() {
        System.out.println("please wait a moment ,we will give you a bottom of water");
    }

    @Override
    public void returnMoney() {
        System.out.println("no money returned cause you have passed the button");
    }

    @Override
    public void passButton() {
        System.out.println("passed the button twice doesn't give another water ");
    }

    @Override
    public void distribute() {
        newVendingMachines.flushWater();
        if (newVendingMachines.getCount() > 0) {
            newVendingMachines.setState(newVendingMachines.getNo1_5State());
        } else {
            newVendingMachines.setState(newVendingMachines.getSoldOutState());
        }
    }
}

添加上赢家的状态

package com.jerry.statepattern.newDisign;

/**
 * @version 1.0
 * @Author jerryLau
 * @Date 2024/4/8 8:46
 * @注释 产生赢家状态
 */
public class WinnerState implements State {
    NewVendingMachines newVendingMachines;

    public WinnerState(NewVendingMachines newVendingMachines) {
        this.newVendingMachines = newVendingMachines;
    }

    @Override
    public void insertMoney() {
        System.out.println("no need to insert money");
    }

    @Override
    public void returnMoney() {
        System.out.println("no money return");
    }

    @Override
    public void passButton() {
        System.out.println("you passed money but no water gives to you ");
    }

    @Override
    public void distribute() {
        System.out.println("YOU'RE a WINNER! You get another water ");
        newVendingMachines.flushWater();
        if (newVendingMachines.getCount() == 0) {
            newVendingMachines.setState(newVendingMachines.getSoldOutState());
        } else {
            newVendingMachines.flushWater();
            if (newVendingMachines.getCount() > 0) {
                newVendingMachines.setState(newVendingMachines.getNo1_5State());
            } else {
                System.out.println("Oops,out of water!");
                newVendingMachines.setState(newVendingMachines.getSoldOutState());
            }
        }
    }
}

那么持有1.5元状态(投币状态)代码就会在进行响应的修改

package com.jerry.statepattern.newDisign;

/**
 * @version 1.0
 * @Author jerryLau
 * @Date 2024/4/8 8:45
 * @注释 被投币状态
 */
public class Has1_5State implements State {
    NewVendingMachines newVendingMachines;

    public Has1_5State(NewVendingMachines newVendingMachines) {
        this.newVendingMachines = newVendingMachines;
    }

    @Override
    public void insertMoney() {
        System.out.println("you have insert money, no more money need insert");
    }

    @Override
    public void returnMoney() {
        System.out.println("money returned");
        newVendingMachines.setState(newVendingMachines.getNo1_5State());
    }

    @Override
    public void passButton() {
        System.out.println("you passed the button...");

        int winner = (int)(Math.random()*10);
        if ((winner == 0) && (newVendingMachines.getCount() > 1)) {
            newVendingMachines.setState(newVendingMachines.getWinnerState());
        } else {
            newVendingMachines.setState(newVendingMachines.getSoldState());
        }
    }

    @Override
    public void distribute() {
        System.out.println("no water distributed");
    }
}

此外呢,贩卖机也做如下修改

package com.jerry.statepattern.newDisign;

/**
 * @version 1.0
 * @Author jerryLau
 * @Date 2024/4/7 16:08
 * @注释 自动售卖机
 */
public class NewVendingMachines {
    State soldState;
    State soldOutState;
    State has1_5State;
    State no1_5State;

    State winnerState;

    State state = soldOutState;
    int count = 0;

    public NewVendingMachines(int count) {

        soldState = new SoldState(this);
        soldOutState = new SoldOutState(this);
        has1_5State = new Has1_5State(this);
        no1_5State = new No1_5State(this);
        winnerState = new WinnerState(this);
        this.count = count;
        if (count > 0) {
            state = no1_5State;
        }
    }

    public void insertMoney() {
        state.insertMoney();
    }

    /***
     * 退币
     */
    public void returnMoney() {
        state.returnMoney();
    }


    /***
     * 按钮按下
     */
    public void passButton() {
        state.passButton();
        state.distribute();
    }

    public void setState(State state) {
        this.state = state;
    }


    public void flushWater() {
        System.out.println("A bottom of water comes rolling out the slot");
        if (count != 0) {
            count = count-1;
        }
    }

    public State getSoldState() {
        return soldState;
    }

    public State getSoldOutState() {
        return soldOutState;
    }

    public State getHas1_5State() {
        return has1_5State;
    }

    public State getNo1_5State() {
        return no1_5State;
    }

    public State getState() {
        return state;
    }

    public State getWinnerState() {
        return winnerState;
    }

    public int getCount() {
        return count;
    }

    //....

    @Override
    public String toString() {
        return "VendingMachines{" +
                "state=" + state +
                ", count=" + count +
                '}';
    }
}

构建本地测试类

public class TestMainDriver {
    public static void main(String[] args) {
        NewVendingMachines gumballMachine = new NewVendingMachines(5);
        System.out.println(gumballMachine);
        gumballMachine.insertMoney();
        gumballMachine.passButton();
        System.out.println(gumballMachine);

    }
}

运行结果

VendingMachines{state=com.jerry.statepattern.newDisign.No1_5State@1b6d3586, count=5}
you insert money
you passed the button…
A bottom of water comes rolling out the slot
VendingMachines{state=com.jerry.statepattern.newDisign.No1_5State@1b6d3586, count=4}

Process finished with exit code 0

很明显哈,我不是一个幸运的人,哈哈哈

至此我们完成了无知老板的需求,也实现了状态设计模式。

我们再次来回顾一下,在上述过程中我们做了些什么

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

这个描述中的第一部分附有相当多的涵义,是吧?因为这个模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象,我们知道行为会随着内部状态而改变。贩卖机提供了一个很好的例子,当贩卖机是在未投币或已投币两种不同的状态时,你投入1.5元钱,就会得到不同的行为(机器接受1.5元和机器拒绝1.5元)。

而这个定义中的第二部分呢?一个对象“看起来好像修改了它的类”是什么意思呢?从客户的视角来看:如果说你使用的对象能够完全改变它的行为,那么你会觉得,这个对象实际上是从别的类实例化而来的。然而,实际上,你知道我们是在使用组合通过简单引用不同的状态对象来造成类改变的假象。

状态模式的类图

请添加图片描述

新需求中,我们补全状态转化图

请添加图片描述

优缺点

优点:
  1. 封装了转换规则:将状态的转换规则分散到多个状态类中,减少了相互之间的依赖。
  2. 易于维护:把状态的判断逻辑转移到了表示不同状态的一系列类中,可以把复杂的判断逻辑简化。
  3. 将行为局部化:每一个状态对应一个子类,而将请求委托给当前状态对象自行处理。
缺点:
  1. 状态过多导致类膨胀:如果状态过多,会造成类个数过多,系统变得庞大。
  2. 设计难度和复杂度:对于可以切换状态的状态机,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

代码相关代码可以参考 gitee代码仓库🌐 || github🤖

ps:本文原创,转载请注明出处


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1576925.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Web漏洞-文件上传常见验证

后缀名:类型,文件头等 后缀名:黑白名单 文件类型:MIME信息 文件头:内容头信息 常见黑名单(明确不允许上传的格式后缀):asp、php、jsp、aspx、cgi、war (如果没有完整…

绿联 安装Frpc内网穿透并使用Nginx反向代理

绿联 安装Frpc内网穿透并使用Nginx反向代理 1、前言 服务器官网:雨云 - 新一代云服务提供商 本教程使用Frps与Frpc进行内网穿透,其中Frps需要自购服务器安装,若无法购买服务器则本教程对你无用; 另外还需拥有自己的域名&#xf…

什么是数据库?如何安装SQL Server(超详细版)

文章目录 什么是数据库数据库与数据库管理系统数据库系统之间的区别和联系数据库在生活中的应用 安装SQL Server数据库系统要求 安装步骤(超详细)安装前的准备 安装SSMS 什么是数据库 数据库,顾名思义,是存储数据的“仓库”。它不仅仅是简单的数据存储&…

软件验收流程

验收环节,甲方需要做哪些事情?这些事情的流程是什么?做这些事情能给甲方带来什么好处? 软件验收阶段,甲方要做的事情: 验收环节介绍 1. 开始 目的:启动验收流程,为后续工作做好准…

第九届蓝桥杯大赛个人赛省赛(软件类)真题C 语言 A 组-航班时间

#include<iostream> using namespace std;int getTime(){int h1, h2, m1, m2, s1, s2, d 0;//d一定初始化为0&#xff0c;以正确处理不跨天的情况 scanf("%d:%d:%d %d:%d:%d (%d)", &h1, &m1, &s1, &h2, &m2, &s2, &d);return d …

【随笔】Git 高级篇 -- 相对引用2 HEAD~n(十三)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

matlab使用教程(37)—求解数值积分(2)

1多项式积分的解析解 本示例显示如何使用 polyint 函数对多项式求解析积分。使用此函数来计算多项式的不定积分。 1.1定义问题 考虑实数不定积分&#xff0c; 其中 k 是积分常量。由于没有指定积分限值&#xff0c; integral 函数族不太适合求解这个问题。 1.2用向量表示多…

量子计算领域迎来了一项令人振奋的突破。

近日&#xff0c;量子计算领域迎来了一项令人振奋的突破。Quantinuum公司与科技巨头Microsoft宣布&#xff0c;双方在容错量子计算方面取得了显著成果。这一成就原本预计需要数年时间才能实现&#xff0c;但两家公司的紧密合作使得这一目标提前达成。 Quantinuum的新一代量子计…

js笔记(学习存档)

JS的调用方式与执行顺序 使用方式 HTML页面中的任意位置加上<script type"module"></script>标签即可。 常见使用方式有以下几种&#xff1a; 直接在<script type"module"></script>标签内写JS代码。直接引入文件&#xff1a;…

2024-4-7 QT day1作业

myWidget.cpp #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//设置窗口标题this->setWindowTitle("QQ");//设置窗口图标this->setWindowIcon(QIcon("C:\\Users\\张谦\\Desktop\\pictrue\\qq.png"));//设…

V R沉浸式体验的应用|R主题馆加盟|V R游戏体验馆加盟

《探索未知世界&#xff0c;沉浸式VR体验引领未来》 随着科技的飞速发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术正逐渐走进人们的生活&#xff0c;并为我们带来前所未有的沉浸式体验。下面让我们一起探索一下&#xff0c;VR沉浸式体验的应用领域和魅力所在。 虚…

安卓开机启动流程

目录 一、整体框架二、流程代码分析2.1 Boot ROM2.2 Boot Loader2.3 Kernel层Kernel代码部分 2.4 Init进程Init进程代码部分 2.5 zygote进程zygote代码部分 2.6 SystemServer进程SystemServer代码部分 2.7 启动Launcher与SystemUI 三、SystemServices3.1 引导服务3.2 核心服务3…

15个最流行的STL模型修复工具

如果你进行 3D 打印&#xff0c;可能遇到过可怕的“无法打印 STL”问题&#xff1a;你的 STL 文件看起来很棒&#xff0c;但它会导致切片机出现问题或导致奇怪的打印错误或完全失败。 无论确切原因是什么&#xff0c;这些问题的根源通常归结为 STL 用于表示 3D 模型的连接三角形…

day 74 js

JS的作用&#xff1a; 修饰 HTML 标签&#xff08;CSS&#xff09;设置 HTML 标签的动作&#xff08;JS&#xff09;JS特点&#xff1a;基于对象的编程语言&#xff0c;通过浏览器解释执行作用&#xff1a;表单验证、改变标签样式、获取和设置标签属性值 1 表单验证: <fo…

Java常用API_正则表达式_字符串的替换和截取方法——小练习

我将通过一个练习题来展示这两个方法 练习题&#xff1a; 有一段字符串&#xff1a;小张qwertyuiop123小李asdfghjkl456小王 要求1&#xff1a;把字符串中三个姓名之间的字母替换成vs 要求2&#xff1a;把字符串中的三个姓名切割出来 编写代码&#xff1a; public class Tes…

MacBook运行缓慢?揭秘一键释放内存的有效秘诀!

当 MacBook 出现运行缓慢的现象&#xff0c;很大程度上可能是由于内存资源耗尽所致。内存&#xff08;RAM&#xff09;作为计算机的核心临时存储区域&#xff0c;负责承载当前活跃的应用程序及其相关数据。一旦内存容量达到饱和&#xff0c;MacBook将不得不依赖交换文件机制&am…

大话设计模式——11.桥接模式(Bridge Pattern)

简介 将抽象部分与它的实现部分分离&#xff0c;使它们可以独立变化。 UML图&#xff1a; 应用场景&#xff1a; 系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性不想使用继承导致系统类的个数急剧增加某个类存在多个变化维度使用继承方式容易出现类的膨胀 示例…

Linux系统下关闭Java进程

关闭Java进程具体分为两步&#xff1a; 1.ps查进程ID 2.kill杀进程 具体的命令如下&#xff1a; ps查进程ID&#xff1a;ps -ef | grep java kill杀进程&#xff1a;kill -9 对应进程号 linux下解除被占用的端口号&#xff1a; 1.查看8088端口是否被占用&#xff1a; netsta…

设计模式之责任链模式讲解

概念&#xff1a;使多个对象都有机会处理请求&#xff0c;从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链&#xff0c;并沿着这条链传递该请求&#xff0c;直到有对象处理它为止。最匹配的场景应该就是逐层审批的模式。 责任链模式只有两个角色&#xff…

基于单片机三路地磁场分量信号采集系统仿真

**单片机设计介绍&#xff0c;基于单片机三路地磁场分量信号采集系统仿真 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机三路地磁场分量信号采集系统仿真概要主要包括以下几个方面&#xff1a;系统组成、信号采集原理…