1 概述

【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。

类图如下:

代码如下:

public interface ILift {//电梯的4个状态//开门状态public final static int OPENING_STATE = 1;//关门状态public final static int CLOSING_STATE = 2;//运行状态public final static int RUNNING_STATE = 3;//停止状态public final static int STOPPING_STATE = 4;//设置电梯的状态public void setState(int state);//电梯的动作public void open();public void close();public void run();public void stop();
}public class Lift implements ILift {private int state;@Overridepublic void setState(int state) {this.state = state;}//执行关门动作@Overridepublic void close() {switch (this.state) {case OPENING_STATE:System.out.println("电梯关门了。。。");//只有开门状态可以关闭电梯门,可以对应电梯状态表来看this.setState(CLOSING_STATE);//关门之后电梯就是关闭状态了break;case CLOSING_STATE://do nothing //已经是关门状态,不能关门break;case RUNNING_STATE://do nothing //运行时电梯门是关着的,不能关门break;case STOPPING_STATE://do nothing //停止时电梯也是关着的,不能关门break;}}//执行开门动作@Overridepublic void open() {switch (this.state) {case OPENING_STATE://门已经开了,不能再开门了//do nothingbreak;case CLOSING_STATE://关门状态,门打开:System.out.println("电梯门打开了。。。");this.setState(OPENING_STATE);break;case RUNNING_STATE://do nothing 运行时电梯不能开门break;case STOPPING_STATE:System.out.println("电梯门开了。。。");//电梯停了,可以开门了this.setState(OPENING_STATE);break;}}//执行运行动作@Overridepublic void run() {switch (this.state) {case OPENING_STATE://电梯不能开着门就走//do nothingbreak;case CLOSING_STATE://门关了,可以运行了System.out.println("电梯开始运行了。。。");this.setState(RUNNING_STATE);//现在是运行状态break;case RUNNING_STATE://do nothing 已经是运行状态了break;case STOPPING_STATE:System.out.println("电梯开始运行了。。。");this.setState(RUNNING_STATE);break;}}//执行停止动作@Overridepublic void stop() {switch (this.state) {case OPENING_STATE: //开门的电梯已经是是停止的了(正常情况下)//do nothingbreak;case CLOSING_STATE://关门时才可以停止System.out.println("电梯停止了。。。");this.setState(STOPPING_STATE);break;case RUNNING_STATE://运行时当然可以停止了System.out.println("电梯停止了。。。");this.setState(STOPPING_STATE);break;case STOPPING_STATE://do nothingbreak;}}
}public class Client {public static void main(String[] args) {Lift lift = new Lift();lift.setState(ILift.STOPPING_STATE);//电梯是停止的lift.open();//开门lift.close();//关门lift.run();//运行lift.stop();//停止}
}

问题分析:

  • 使用了大量的switch…case这样的判断(if…else也是一样),使程序的可阅读性变差。
  • 扩展性以及维护性都很差。如果新加了断电的状态,我们需要修改上面判断逻辑

为了解决这些问题,我们可以使用状态模式,在状态模式中,我们将对象在每一个状态下的行为和状态转移语句封装在一个个状态类中,通过这些状态类来分散冗长的条件转移语句,让系统具有更好的灵活性和可扩展性,状态模式可以在一定程度上解决上述问题

也就是说 状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。

状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。

**基于与策略模式相同的 考虑,**首先将以上的类中每个状态的功能单独封装在一个类中。这样,就得到一个和策略模式相 同结构的类的结构体,称为状态模式。

状态模式定义:

对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

6.5.2 结构

状态模式包含以下主要角色。

  • 环境(Context)角色:也叫环境类, 又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。

    环境类实际上是真正拥有状态的对象,我们只是将环境类中与状态有关的代码提取出来封装到专门的状态类中。

  • 抽象状态(State)角色: 它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。

  • 具体状态(Concrete State)角色: 它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。

    在状态模式中,我们将对象在不同状态下的行为封装到不同的状态类中,为了让系统具有更好的灵活性和可扩展性,同时对各状态下的共有行为进行封装,我们需要对状态进行抽象,引入了抽象状态类角色 。

6.5.3 案例实现

对上述电梯的案例使用状态模式进行改进。类图如下:

代码如下:

//抽象状态类
public abstract class LiftState {//定义一个环境角色,也就是封装状态的变化引起的功能变化protected Context context;public void setContext(Context context) {this.context = context;}//电梯开门动作public abstract void open();//电梯关门动作public abstract void close();//电梯运行动作public abstract void run();//电梯停止动作public abstract void stop();
}//开启状态
public class OpenningState extends LiftState {//开启当然可以关闭了,我就想测试一下电梯门开关功能@Overridepublic void open() {System.out.println("电梯门开启...");}@Overridepublic void close() {//状态修改super.context.setLiftState(Context.closeingState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}//电梯门不能开着就跑,这里什么也不做@Overridepublic void run() {//do nothing}//开门状态已经是停止的了@Overridepublic void stop() {//do nothing}
}//运行状态
public class RunningState extends LiftState {//运行的时候开电梯门?你疯了!电梯不会给你开的@Overridepublic void open() {//do nothing}//电梯门关闭?这是肯定了@Overridepublic void close() {//虽然可以关门,但这个动作不归我执行//do nothing}//这是在运行状态下要实现的方法@Overridepublic void run() {System.out.println("电梯正在运行...");}//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了@Overridepublic void stop() {super.context.setLiftState(Context.stoppingState);super.context.stop();}
}//停止状态
public class StoppingState extends LiftState {//停止状态,开门,那是要的!@Overridepublic void open() {//状态修改super.context.setLiftState(Context.openningState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().open();}@Overridepublic void close() {//虽然可以关门,但这个动作不归我执行//状态修改super.context.setLiftState(Context.closeingState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}//停止状态再跑起来,正常的很@Overridepublic void run() {//状态修改super.context.setLiftState(Context.runningState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().run();}//停止状态是怎么发生的呢?当然是停止方法执行了@Overridepublic void stop() {System.out.println("电梯停止了...");}
}//关闭状态
public class ClosingState extends LiftState {@Override//电梯门关闭,这是关闭状态要实现的动作public void close() {System.out.println("电梯门关闭...");}//电梯门关了再打开,逗你玩呢,那这个允许呀@Overridepublic void open() {super.context.setLiftState(Context.openningState);super.context.open();}//电梯门关了就跑,这是再正常不过了@Overridepublic void run() {super.context.setLiftState(Context.runningState);super.context.run();}//电梯门关着,我就不按楼层@Overridepublic void stop() {super.context.setLiftState(Context.stoppingState);super.context.stop();}
}//环境角色
public class Context {//定义出所有的电梯状态public final static OpenningState openningState = new OpenningState();//开门状态,这时候电梯只能关闭public final static ClosingState closeingState = new ClosingState();//关闭状态,这时候电梯可以运行、停止和开门public final static RunningState runningState = new RunningState();//运行状态,这时候电梯只能停止public final static StoppingState stoppingState = new StoppingState();//停止状态,这时候电梯可以开门、运行//定义一个当前电梯状态private LiftState liftState;public LiftState getLiftState() {return this.liftState;}public void setLiftState(LiftState liftState) {//当前环境改变this.liftState = liftState;//把当前的环境通知到各个实现类中this.liftState.setContext(this);}public void open() {this.liftState.open();}public void close() {this.liftState.close();}public void run() {this.liftState.run();}public void stop() {this.liftState.stop();}
}//测试类
public class Client {public static void main(String[] args) {Context context = new Context();context.setLiftState(new ClosingState());context.open();context.close();context.run();context.stop();}
}

6.5.4 优缺点

状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象可以让环境对象拥有不同的行为,而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。在实际开发中,状态模式具有较高的使用频率,在工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。

1,优点:

  • 封装了状态的转换规则,在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,可以对状态转换代码进行集中管理,而不是分散在一个个业务方法中。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
  • 可以****让多个环境对象共享一个状态对象****,从而减少系统中对象的个数。

2,缺点:

  • 状态模式的使用必然会增加系统类和对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  • 状态模式对"开闭原则"的支持并不太好。 增加新的状态类需要修改那些负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

6.5.5 使用场景

  • 对象的行为依赖于它的状态(如某些属性值),状态的改变将导致行为的变化。
  • 在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强。

策略模式和状态模式的区别

策略模式用来处理一组具有相同目的但是实现方法不同的算法,这些算法方案之间一般来 说没有状态变迁,并且用户总是从几个算法中间选取一个。例如,以不同的格式保存文件,以不 同的算法压缩文件 以不同的算法排序,以不同的格式绘制同样数据的图形,以不同的方法显示 信息 等等。

状态模式则有所不同,它实现的一个概念可以叫做动态继承,也就是继承的( 代表一个状 态)子类都可以发生变化。状态的变化可以由一个状态迁移图表示。在实现中,如果状态的变 化由状态类完成,则在整个变化过程中某些状态之间需要插入一个新的状态或者状态的顺序之 间发生变化, 客户程序是根本不用改变的,即对客户是不可见的。

一般地说,使用状态模式要比使用策略模式在设计与实现方面会更加复杂一些,原因是用户 需要仔细地考虑由谁负责状态转换问题,是由 Context 类负责还是由状态类的超类负责 还是由 状态类的子类负责等。

设计模式之状态模式详解相关推荐

  1. android 状态模式,Android编程设计模式之状态模式详解

    本文实例讲述了Android编程设计模式之状态模式.分享给大家供大家参考,具体如下: 一.介绍 状态模式中的行为是由状态来决定的,不同的状态下有不同的行为.状态模式和策略模式的结构几乎完全一样,但它们 ...

  2. java状态机设计模式_Java设计模式之状态模式详解

    (本文由言念小文原创,转载请注明出处) 在实际工作中经常遇到某个对象,处于不同的状态有不同行为逻辑.且状态之间可以相互迁移的业务场景,特别是在开发通信协议栈类软件中尤为多见.<设计模式之禅> ...

  3. 设计模式之状态模式详解(State Pattern)

    在软件开发过程中,应用程序中的有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态.当有状态的对象与外部事件产生互动时,其内部 ...

  4. 设计模式——状态模式详解

    0. 前言 写在最前面,本人的设计模式类博文,建议先看博文前半部分的理论介绍,再看后半部分的实例分析,最后再返回来复习一遍理论介绍,这时候你就会发现我在重点处标红的用心,对于帮助你理解设计模式有奇效哦 ...

  5. (二十一)状态模式详解(DOTA版) - 转

    作者:zuoxiaolong8810(左潇龙),转载请注明出处. 本次LZ给各位介绍状态模式,之前在写设计模式的时候,引入了一些小故事,二十章职责连模式是故事版的最后一篇,之后还剩余四个设计模式,LZ ...

  6. 设计模式之模板方法模式详解

    设计模式之模板方法模式详解 概述 在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的 ...

  7. 设计模式之门面模式详解

    设计模式之门面模式详解 文章目录 设计模式之门面模式详解 一.什么是门面模式 二.门面模式的应用场景 三.门面模式的角色组成 四.门面模式通用写法 五.门面模式在业务中的应用 六.门面模式优缺点 一. ...

  8. 设计模式——模版方法模式详解(论沉迷LOL对学生的危害)

    0. 前言 写在最前面,本人的设计模式类博文,建议先看博文前半部分的理论介绍,再看后半部分的实例分析,最后再返回来复习一遍理论介绍,这时候你就会发现我在重点处标红的用心,对于帮助你理解设计模式有奇效哦 ...

  9. 设计模式之桥接模式详解

    设计模式之桥接模式详解 文章目录 设计模式之桥接模式详解 一.什么是桥接模式 二.桥接模式的应用场景 三.桥接模式的角色组成 四.桥接模式通用写法示例 五.桥接模式优缺点 一.什么是桥接模式 桥接模式 ...

最新文章

  1. ICML 2020 | Google提出最强生成式摘要预训练模型——天马
  2. static变量初始化
  3. xml解析:Sax,Dom,pull解析
  4. 混淆视听的感脚(二)
  5. 使用await和async关键字开发nodejs应用批量取出简书网站的文章标题和超链接
  6. python正确打开方式_Python log 的正确打开方式
  7. c++11 多线程 2c++ concurrency in action
  8. php 获取上周日期_php 获取上一周下一周的日期列表
  9. WebLogic的下载与安装(图文教程)
  10. [导入]Asp.Net小技巧集合
  11. event事件的坐标 offsetWidth client scroll
  12. VS2005项目的安装与布署,包括卸载
  13. 专线维护 07/11
  14. Chrome 崩溃 相关
  15. HEVC 推出专利使用费标准
  16. win7系统可以禁止的服务器,win7旗舰版怎么禁用不必要的系统服务项
  17. 导线水平角及边长观测实习记录
  18. BetaFlight模块设计之十三:Gyro过滤任务分析
  19. win7旗舰版系统下载
  20. DataSource

热门文章

  1. 猪八戒背景比孙悟空还要厉害,只凭猪八戒的师傅,就可以横行三界
  2. 开发工程师必备————【Day14】数据库查询表关键字及多表查询思路
  3. Petsc求解一维非线性微分方程,SNES对象-2
  4. swt eclipse 3.6
  5. 给俺的B189换换那该死的铃声。。。
  6. 阿里巴巴CEO张勇:数字化是奠定未来发展的基石
  7. 学习太极创客 — ESP8226 (七)JSON 数据通讯 一
  8. 基于Python的2013-2018全国城市空气质量分析
  9. 2009年世界大学排行
  10. 如何利用Python破解12306验证码和浏览车次为例!你学会了吗?