当前位置: 首页 > news >正文

设计模式(十五)状态模式

请直接看原文:
设计模式系列

------------------------------------------------------------------------------------------------------------------------------- 

前言

建议在阅读本文前先阅读设计模式(十一)策略模式这篇文章,虽说状态模式和策略模式的结构几乎是相同的,但是它们所解决的问题是不同的,读完这两篇文章你就会有了答案。

1.状态模式定义
状态模式定义

定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

状态模式UML图

在享元模式中有如下角色:

  • Context:环境角色,定义客户端需要的接口,并且负责具体状态的切换。
  • State:抽象状态角色,可以是抽象类或者接口,负责对象状态定义,并封装了环境角色。
  • ConcreteState:具体状态角色,实现抽象角色类,定义了本状态所要做的事情。
2.简单实现状态模式

拿用mp3听歌来说,mp3有四种基本状态,分别是开机、关机、上一首歌和下一首歌。如果我们要写一个对mp3进行控制的类,你可能会这样写,如下所示。

public class Mp3Controller {private static final int POWER_ON = 1;private static final int POWER_OFF = 2;private int state = POWER_OFF;public void powerOn() {if (state == POWER_OFF) {System.out.println("开机");}state = POWER_ON;}public void powerOff() {if (state == POWER_ON) {System.out.println("关机");}state = POWER_OFF;}public void preSong() {if (state == POWER_ON) {System.out.println("上一首歌");}}public void nextSong() {if (state == POWER_ON) {System.out.println("下一首歌");}}
}

在powerOn和powerOff方法中我们会将state置为相应的状态,在preSong和nextSong方法中,首先要判断当前mp3的state,如果是POWER_OFF,则不做任何处理,写到这里你可能会觉得实现很简单啊。那么我再添加些状态,比如待机状态、休眠状态、亮屏状态等等,顺便再添加些功能,比如调大音量、调小音量、降噪等。这样你实现起来,就会发现你会定义很多种状态,在功能中可能要用到多个条件语句进行判断,这会使得代码变得臃肿。
状态模式就是为了解决这一问题,将多个条件语句去掉,使得代码更加清晰,下面来进行实现。

抽象状态角色
public interface Mp3State {//开机public void powerOn();//关机public void powerOff();//上一首歌曲public void preSong();//下一首歌曲public void nextSong();
}

接口Mp3State中定义了四种功能,接下来我们来实现Mp3State。

具体状态角色

我们先来实现开机状态,代码如下所示。

public class PowerOnState implements Mp3State {@Overridepublic void powerOn() {System.out.println("已开机");}@Overridepublic void powerOff() {System.out.println("关机");}@Overridepublic void preSong() {System.out.println("上一首歌");}@Overridepublic void nextSong() {System.out.println("下一首歌");}
}

比较特殊的是powerOn方法中,打印了“已开机”,因为在PowerOnState 状态下进行开机操作是多此一举的。
接着实现关机状态:

public class PowerOffState implements Mp3State {@Overridepublic void powerOn() {System.out.println("开机");}@Overridepublic void powerOff() {}@Overridepublic void preSong() {}@Overridepublic void nextSong() {}
}

在关机状态中只实现了powerOn方法,其他的方法都是空实现。

环境角色
public class Context {private Mp3State mp3State;public void setMp3State(Mp3State mp3State){this.mp3State=mp3State;}public void powerOn(){mp3State.powerOn();setMp3State(new PowerOnState());}public void powerOff(){mp3State.powerOff();setMp3State(new PowerOffState());}public void preSong(){mp3State.preSong();}public void nextSong(){mp3State.nextSong();}
}

Context 中定义了setMp3State方法,用来设定状态,其中powerOn方法中会调用setMp3State方法将状态置为PowerOffState,同理powerOff中将状态置为PowerOffState。

客户端调用
public class Client {public static void main(String[] args){Context context=new Context();context.setMp3State(new PowerOffState());context.preSong();context.powerOn();context.nextSong();context.powerOff();}
}

我们只需要先设定mp3的初始状态,就可以调用各种功能方法了,不需要再考虑功能和状态之间的关系。输出结果为:
开机
下一首歌
关机

虽然这个例子的代码很简单,这里还是给出UML图,如下所示。

3.状态模式的使用场景和优缺点
优点
  • 避免了过多的条件语句,使得结构更清晰,提高代码的可维护性。
  • 每个状态都是一个子类,方便增加和修改状态。
  • 状态被放置到类的内部,外部调用不需要知道类的内部如何实现状态和行为的变换。
缺点
  • 完全使用状态模式,可能会导致子类会过多。
使用场景
  • 代码中包含大量与对象状态有关的条件语句。
  • 对象的行为依赖着状态,并且行为随着状态的改变而改变。

相关文章:

  • Flask基于配置文件添加项目config配置
  • Selenium 4.0+ 版本的“正确使用”以及“驱动程序的正确安装”
  • TinyEMU编译与使用
  • 二分以及练习题目
  • wordpress外贸独立站
  • laravel-admin 头部添加操作
  • openssl3.2 - exp - 产生随机数
  • 本地部署websocket服务端并结合内网穿透实现固定公网地址连接
  • 文本溢出隐藏 显示省略号,鼠标悬浮展示 el-tooltip(TooltipIsShowMixin封装)
  • 智能驾驶规划控制理论学习07-规划算法整体框架
  • Ps:历史记录面板
  • 前端常见的安全问题以及处理措施
  • STC89C52串口通信详解
  • 虚拟化相关面试题集锦(1)—— 如何在QEMU/KVM中跑通AI相关训练
  • docker安装php7.4安装(swoole)
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • Angular Elements 及其运作原理
  • exports和module.exports
  • HashMap剖析之内部结构
  • idea + plantuml 画流程图
  • JAVA SE 6 GC调优笔记
  • Java读取Properties文件的六种方法
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • miaov-React 最佳入门
  • vuex 学习笔记 01
  • 爱情 北京女病人
  • 警报:线上事故之CountDownLatch的威力
  • 聊聊flink的BlobWriter
  • 区块链分支循环
  • 全栈开发——Linux
  • 使用common-codec进行md5加密
  • 数据结构java版之冒泡排序及优化
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 跳前端坑前,先看看这个!!
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • 小程序开发之路(一)
  • 怎么把视频里的音乐提取出来
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • #pragma预处理命令
  • #宝哥教你#查看jquery绑定的事件函数
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (C语言)球球大作战
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (考研湖科大教书匠计算机网络)第一章概述-第五节1:计算机网络体系结构之分层思想和举例
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)linux 命令大全
  • (转)程序员疫苗:代码注入
  • (转)我也是一只IT小小鸟
  • .NET MVC之AOP
  • .net 获取url的方法
  • .NET/C# 使用反射注册事件
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter