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

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

日常叨逼叨

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:本文原创,转载请注明出处


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue3中对v-md-editor编辑器的集成使用

效果展示: 首先安装需要的包 npm i kangc/v-md-editornext -S 在main.js中进行全局配置 import VMdEditor from kangc/v-md-editor/lib/codemirror-editor; import kangc/v-md-editor/lib/style/codemirror-editor.css; import githubTheme from kangc/v-md-ed…

学透Spring Boot — 005. 深入理解 Spring Boot Starter 依赖管理

前面的文章直观的展示了,使用Spring Boot 集成 Hibernate 和手动集成 Hibernate 之间差距。 一个比喻 工作中使用过Spring Boot一段时间后,我越来越感觉到Spring Boot Starter带来的便利性。 使用手动集成 Hibernate, 就像去电脑城配电脑&…

怎么防止文件被拷贝,复制别人拷贝电脑文件

怎么防止文件被拷贝,复制别人拷贝电,脑文件 防止文件被拷贝通常是为了保护敏感数据、知识产权或商业秘密不被未经授权的人员获取或传播。以下列出了一系列技术手段和策略,可以帮助您有效地防止文件被拷贝。 1. 终端管理软件: 如安企神、域智…

IP地址到底有什么用

IP地址在计算机网络中的作用至关重要,它不仅是设备在网络中的唯一标识,更是实现网络通信、网络管理和安全的关键要素。下面,我们将从多个方面详细阐述IP地址的作用。 首先,IP地址作为设备的唯一标识,为网络通信提供了…

ssm032基于Java的汽车客运站管理系统的设计与实现+jsp

汽车客运站管理系统的设计与实现 摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对汽车客运站售票信息管理混乱&…

MP4视频如何转OGV视频格式?视频格式转换的方法

一,什么是OGV视频格式 OGV是一个使用OGG开源格式的容器。 OGG不受软件专利的限制,这是其创建的主要目标之一。 OGV格式用于存储带或不带音频的视频流,而视频流又可以用Opus,Vorbis,Theora或Speex算法压缩。该格式用于…

门电路知识点总结

目录 一、基本门电路 (Gate Cricuit) 1.与电路 2.或电路 3.非电路 二、复合门电路 (Composite gate circuit) 1.与非门 2.或非门 3.与或非门 4.异或门 5.同或门 三、CMOS门电路 (Complementary Metal-Oxide-Semiconduc…

J基于微信小程序的电影订票、电影购票小程序

文章目录 1 **摘 要**2 技术简介**3 系统功能设计****第4章 系统设计****4.1系统结构设计** 第5章 系统实现**5.1管理员服务端功能模块**5.2用户客户端功能模块 结 论6 推荐阅读7 源码获取: 1 摘 要 本文从管理员、用户的功能要求出发,电影订票系统小程…

PSO-SVM,基于PSO粒子群算法优化SVM支持向量机回归预测(多输入单输出)-附代码

PSO-SVM是一种结合了粒子群优化(Particle Swarm Optimization, PSO)算法和支持向量机(Support Vector Machine, SVM)的方法,用于回归预测问题。下面我将解释PSO-SVM的原理: 1、支持向量机(SVM&a…

【SCI绘图】【热力图系列2 R】多特征相关性分析热力图指定聚类 R

SCI,CCF,EI及核心期刊绘图宝典,爆款持续更新,助力科研! 本期分享: 【SCI绘图】【热力图系列2 R】多特征相关性分析热力图指定聚类 R 1.环境准备 library(gplots) library(RColorBrewer)2.数据读取 ###…

分类预测 | Matlab实现OOA-BP鱼鹰算法优化BP神经网络数据分类预测

分类预测 | Matlab实现OOA-BP鱼鹰算法优化BP神经网络数据分类预测 目录 分类预测 | Matlab实现OOA-BP鱼鹰算法优化BP神经网络数据分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现OOA-BP鱼鹰算法优化BP神经网络多特征分类预测(完整源码和数…

OpenHarmony实战:Combo解决方案之W800芯片移植案例

本方案基于OpenHarmony LiteOS-M内核,使用联盛德W800芯片的润和软件海王星系列Neptune100开发板,进行开发移植。 移植架构采用Board与SoC分离方案,支持通过Kconfig图形化配置编译选项,增加玄铁ck804ef架构移植,实现了…

JavaScript - 请你为数组自定义一个方法myFind,使其实现find方法的功能

难度级别:中级及以上 提问概率:50% 我们知道数组的find方法是ES6之后出现的,它强调找到第一个符合条件的元素后即跳出循环,不再继续执行,那么如果不用ES6的知识,为数组添加一个自定义方法实现find方法的功能,首先要想到在数组的原型pro…

JVM的简单介绍

目录 一、JVM的简单介绍 JVM的执行流程 二、JVM中的内存区域划分 1、堆(只有一份) 2、栈(可能有N份) 3、程序计数器(可能有N份) 4、元数据区(只有一份) 经典笔试题 三、JVM…

python:卷积网络实现人脸识别,dlib (也可以用openCV)

一.项目简介 1.数据 数据下载链接人脸识别数据集_免费高速下载|百度网盘-分享无限制 数据集:总共数据集由两部分组成:他人脸图片集及我自己的部分图片 自己图片目录:face_recog/my_faces 他人图片目录:face_recog/other_faces 我…

2024-04-08 NO.5 Quest3 手势追踪进行 UI 交互

文章目录 1 玩家配置2 物体配置3 添加视觉效果4 添加文字5 其他操作5.1 双面渲染5.2 替换图片 ​ 在开始操作前,我们导入先前配置好的预制体 MyOVRCameraRig,相关介绍在 《2024-04-03 NO.4 Quest3 手势追踪抓取物体-CSDN博客》 文章中。 1 玩家配置 &a…

Java-接口-定义接口Filter及其实现类WordFilter

所谓:“纸上得来终觉浅,绝知此事要躬行。” 关于接口的知识,可以几分钟过一遍:Java-接口—知识(基础)-CSDN博客 现在就是练习time,先来看题: 定义一个接口 Filter,表示…

探索算力(云计算、人工智能、边缘计算等):数字时代的引擎

引言 在数字时代,算力是一种至关重要的资源,它是推动科技创新、驱动经济发展的关键引擎之一。简而言之,算力即计算能力,是计算机系统在单位时间内完成的计算任务数量或计算复杂度的度量。随着科技的不断发展和应用范围的不断扩大…

Java基础笔记(一)

一、面向对象高级基础 1.Java的动态绑定机制 public class DynamicBinding {public static void main(String[] args) {//a 的编译类型 A, 运行类型 BA a new B();//向上转型System.out.println(a.sum());//40 子类sum()注释后-> 30System.out.println(a.sum1());//30 子类…

鸿蒙实战开发-相机和媒体库、如何在eTS中调用相机拍照和录像

介绍 此Demo展示如何在eTS中调用相机拍照和录像,以及使用媒体库接口将图片和视频保存的操作。实现效果如下: 效果预览 使用说明 1.启动应用,在权限弹窗中授权后返回应用,进入相机界面。 2.相机界面默认是拍照模式,…