作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可。

           本次LZ给各位介绍状态模式,之前在写设计模式的时候,引入了一些小故事,二十章职责连模式是故事版的最后一篇,之后还剩余四个设计模式,LZ会依照原生的方式去解释这几个设计模式,特别是原型模式和解释器模式,会包含一些其它的内容。好了,接下来,我们先来看看状态模式的定义吧。定义:(源于Design Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。上述是百度百科中对状态模式的定义,定义很简单,只有一句话,请各位形象的去理解这句话,它说当状态改变时,这个对象的行为也会变,而看起来就像是这个类改变了一样。这正是应验了我们那句话,有些人一旦发生过什么事以后,就像变了个人似的,这句话其实与状态模式有异曲同工之妙。我们仔细体会一下定义当中的要点。1、有一个对象,它是有状态的。2、这个对象在状态不同的时候,行为不一样。3、这些状态是可以切换的,而非毫无关系。前两点比较好理解,第3点有时候容易给人比较迷惑的感觉,什么叫这些状态是可以切换的,而非毫无关系?举个例子,比如一个人的状态,可以有很多,像生病和健康,这是两个状态,这是有关系并且可以转换的两个状态。再比如,睡觉、上班、休息,这也算是一组状态,这三个状态也是有关系的并且可以互相转换。那如果把生病和休息这两个状态放在一起,就显得毫无意义了。所以这些状态应该是一组相关并且可互相切换的状态。下面我们来看看状态模式的类图。类图中包含三个角色。Context:它就是那个含有状态的对象,它可以处理一些请求,这些请求最终产生的响应会与状态相关。State:状态接口,它定义了每一个状态的行为集合,这些行为会在Context中得以使用。ConcreteState:具体状态,实现相关行为的具体状态类。如果针对刚才对于人的状态的例子来分析,那么人(Person)就是Context,状态接口依然是状态接口,而具体的状态类,则可以是睡觉,上班,休息,这一系列状态。不过LZ也看过不少状态模式的文章和帖子,包括《大话设计模式》当中,都举的是有关人的状态的例子,所以这里给大家换个口味,我们换一个例子。我们来试着写一个DOTA的例子,最近貌似跟DOTA干上了,不为其他,就因为DOTA伴随了LZ四年的大学时光。玩过的朋友都知道,DOTA里的英雄有很多状态,比如正常,眩晕,加速,减速等等。相信就算没有玩过DOTA的朋友们,在其它游戏里也能见到类似的情况。那么假设我们的DOTA没有使用状态模式,则我们的英雄类会非常复杂和难以维护,我们来看下,原始版的英雄类是怎样的。

复制代码

package com.state;

//英雄类
public class Hero {

public static final int COMMON = 1;//正常状态public static final int SPEED_UP = 2;//加速状态public static final int SPEED_DOWN = 3;//减速状态public static final int SWIM = 4;//眩晕状态private int state = COMMON;//默认是正常状态private Thread runThread;//跑动线程//设置状态
public void setState(int state) {this.state = state;
}
//停止跑动
public void stopRun() {if (isRunning()) runThread.interrupt();System.out.println("--------------停止跑动---------------");
}
//开始跑动
public void startRun() {if (isRunning()) {return;}final Hero hero = this;runThread = new Thread(new Runnable() {public void run() {while (!runThread.isInterrupted()) {try {hero.run();} catch (InterruptedException e) {break;}}}});System.out.println("--------------开始跑动---------------");runThread.start();
}private boolean isRunning(){return runThread != null && !runThread.isInterrupted();
}
//英雄类开始奔跑
private void run() throws InterruptedException{if (state == SPEED_UP) {System.out.println("--------------加速跑动---------------");Thread.sleep(4000);//假设加速持续4秒state = COMMON;System.out.println("------加速状态结束,变为正常状态------");}else if (state == SPEED_DOWN) {System.out.println("--------------减速跑动---------------");Thread.sleep(4000);//假设减速持续4秒state = COMMON;System.out.println("------减速状态结束,变为正常状态------");}else if (state == SWIM) {System.out.println("--------------不能跑动---------------");Thread.sleep(2000);//假设眩晕持续2秒state = COMMON;System.out.println("------眩晕状态结束,变为正常状态------");}else {//正常跑动则不打印内容,否则会刷屏}
}

}

复制代码

            下面我们写一个客户端类,去试图让英雄在各种状态下奔跑一下。

复制代码

package com.state;

public class Main {

public static void main(String[] args) throws InterruptedException {Hero hero = new Hero();hero.startRun();hero.setState(Hero.SPEED_UP);Thread.sleep(5000);hero.setState(Hero.SPEED_DOWN);Thread.sleep(5000);hero.setState(Hero.SWIM);Thread.sleep(5000);hero.stopRun();
}

}

复制代码

            可以看到,我们的英雄在跑动过程中随着状态的改变,会以不同的状态进行跑动。在上面原始的例子当中,我们的英雄类当中有明显的if else结构,我们再来看看百度百科中状态模式所解决的问题的描述。状态模式解决的问题:状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。不用说,状态模式是可以解决我们上面的if else结构的,我们采用状态模式,利用多态的特性可以消除掉if else结构。这样所带来的好处就是可以大大的增加程序的可维护性与扩展性。下面我们就使用状态模式对上面的例子进行改善,首先第一步,就是我们需要定义一个状态接口,这个接口就只有一个方法,就是run。

复制代码

package com.state;

public interface RunState {

void run(Hero hero);

}

复制代码

            与状态模式类图不同的是,我们加入了一个参数Hero(Context),这样做的目的是为了具体的状态类当达到某一个条件的时候可以切换上下文的状态。下面列出四个具体的状态类,其实就是把if else拆掉放到这几个类的run方法中。

复制代码

package com.state;

public class CommonState implements RunState{

public void run(Hero hero) {//正常跑动则不打印内容,否则会刷屏
}

}

复制代码
复制代码

package com.state;

public class SpeedUpState implements RunState{

public void run(Hero hero) {System.out.println("--------------加速跑动---------------");try {Thread.sleep(4000);//假设加速持续4秒} catch (InterruptedException e) {}hero.setState(Hero.COMMON);System.out.println("------加速状态结束,变为正常状态------");
}

}

复制代码
复制代码

package com.state;

public class SpeedDownState implements RunState{

public void run(Hero hero) {System.out.println("--------------减速跑动---------------");try {Thread.sleep(4000);//假设减速持续4秒} catch (InterruptedException e) {}hero.setState(Hero.COMMON);System.out.println("------减速状态结束,变为正常状态------");
}

}

复制代码
复制代码

package com.state;

public class SwimState implements RunState{

public void run(Hero hero) {System.out.println("--------------不能跑动---------------");try {Thread.sleep(2000);//假设眩晕持续2秒} catch (InterruptedException e) {}hero.setState(Hero.COMMON);System.out.println("------眩晕状态结束,变为正常状态------");
}

}

复制代码

            这下我们的英雄类也要相应的改动一下,最主要的改动就是那些if else可以删掉了,如下。

复制代码

package com.state;

//英雄类
public class Hero {

public static final RunState COMMON = new CommonState();//正常状态public static final RunState SPEED_UP = new SpeedUpState();//加速状态public static final RunState SPEED_DOWN = new SpeedDownState();//减速状态public static final RunState SWIM = new SwimState();//眩晕状态private RunState state = COMMON;//默认是正常状态private Thread runThread;//跑动线程//设置状态
public void setState(RunState state) {this.state = state;
}
//停止跑动
public void stopRun() {if (isRunning()) runThread.interrupt();System.out.println("--------------停止跑动---------------");
}
//开始跑动
public void startRun() {if (isRunning()) {return;}final Hero hero = this;runThread = new Thread(new Runnable() {public void run() {while (!runThread.isInterrupted()) {state.run(hero);}}});System.out.println("--------------开始跑动---------------");runThread.start();
}private boolean isRunning(){return runThread != null && !runThread.isInterrupted();
}

}

复制代码

            可以看到,现在我们的英雄类优雅了许多,我们使用刚才同样的客户端运行即可得到同样的结果。对比我们的原始例子,现在我们使用状态模式之后,有几个明显的优点:一、我们去掉了if else结构,使得代码的可维护性更强,不易出错,这个优点挺明显,如果试图让你更改跑动的方法,是刚才的一堆if else好改,还是分成了若干个具体的状态类好改呢?答案是显而易见的。二、使用多态代替了条件判断,这样我们代码的扩展性更强,比如要增加一些状态,假设有加速20%,加速10%,减速10%等等等(这并不是虚构,DOTA当中是真实存在这些状态的),会非常的容易。                三、状态是可以被共享的,这个在上面的例子当中有体现,看下Hero类当中的四个static final变量就知道了,因为状态类一般是没有自己的内部状态的,所有它只是一个具有行为的对象,因此是可以被共享的。四、状态的转换更加简单安全,简单体现在状态的分割,因为我们把一堆if else分割成了若干个代码段分别放在几个具体的状态类当中,所以转换起来当然更简单,而且每次转换的时候我们只需要关注一个固定的状态到其他状态的转换。安全体现在类型安全,我们设置上下文的状态时,必须是状态接口的实现类,而不是原本的一个整数,这可以杜绝魔数以及不正确的状态码。状态模式适用于某一个对象的行为取决于该对象的状态,并且该对象的状态会在运行时转换,又或者有很多的if else判断,而这些判断只是因为状态不同而不断的切换行为。上面的适用场景是很多状态模式的介绍中都提到的,下面我们就来看下刚才DOTA中,英雄例子的类图。可以看到,这个类图与状态模式的标准类图是几乎一模一样的,只是多了一条状态接口到上下文的依赖线,而这个是根据实际需要添加的,而且一般情况下都是需要的。状态模式也有它的缺点,不过它的缺点和大多数模式相似,有两点。1、会增加的类的数量。2、使系统的复杂性增加。尽管状态模式有着这样的缺点,但是往往我们牺牲复杂性去换取的高可维护性和扩展性是相当值得的,除非增加了复杂性以后,对于后者的提升会乎其微。状态模式在项目当中也算是较经常会碰到的一个设计模式,但是通常情况下,我们还是在看到if else的情况下,对项目进行重构时使用,又或者你十分确定要做的项目会朝着状态模式发展,一般情况下,还是不建议在项目的初期使用。 原文出处:http://www.cnblogs.com/zuoxiaolong/p/pattern22.html

(二十一)状态模式详解(DOTA版)相关推荐

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

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

  2. (十二)命令模式详解(故事版)- 转

    作者:zuoxiaolong8810(左潇龙),转载请注明出处. 背景:小左是魔都某公司技术部的一名屌丝程序猿,每天的工作就是维护一个20世纪的古董级项目,由于公司不大,所以公司很多制度不太完善,导致 ...

  3. (二十二)访问者模式详解(伪动态双分派) - 转

    作者:zuoxiaolong8810(左潇龙),转载请注明出处. 本次LZ和各位分享一下访问者模式,从场景.设计初衷以及实现方面来说,访问者模式算是LZ即将写到的24种设计模式当中,最复杂也是最难理解 ...

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

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

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

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

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

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

  7. 设计模式之状态模式详解

    1 概述 [例]通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态.每一种状态改变,都有可能要根据其他状态来更新处理.例如,如果电梯门现在处于运行时状态,就不能进行开门操作 ...

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

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

  9. (十二)命令模式详解(故事版)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处. 背景:小左是魔都某公司技术部的一名屌丝程序猿,每天的工作就是维护一个20世纪的古董级项目,由于公司不大,所以公司很多制度不太完善,导致 ...

最新文章

  1. 神经网络无法区分异同,而且这个缺陷是本质性的
  2. JAVA——prepareStatement中SQL语句中占位符(?)替换表名和字段名
  3. 云原生不仅颠覆了技术栈,背后的每个岗位也在悄然发生改变
  4. python中new方法详解及_Python中new方法的详解
  5. 微软开发者的年度回顾
  6. Spring MVC教程
  7. MFC学习之路之多媒体 --(1) DirectShow
  8. vue项目请求封装;axios封装使用
  9. 16 操作系统第四章 文件管理 文件的基本操作 文件共享 文件保护 文件系统的层次结构
  10. 7张图讲透Java垃圾回收算法!学妹直呼666!!!
  11. Mybatis SQL 语句中 IF函数不支持
  12. 实现人工智能价值的障碍有哪些?
  13. 2018 大数据学习入门必备规划
  14. 【有限元分析】提高有限元分析计算精度的h方法和p方法
  15. antdesign 所兼容的浏览器_牛人推荐的跨浏览器兼容性总结
  16. 线性回归:异方差检测及其处理方法
  17. 最新最火最流行的抖音火山上热门技术!
  18. 洛谷P1512伊甸园的日历游戏题解
  19. 洛谷 P5713 【深基3.例5】洛谷团队系统
  20. 猿辅导python大纲_数据解读独角兽企业“猿辅导”(第一部分)

热门文章

  1. docker入门一:docker安装、基本命令和redis操作示例
  2. IntelliJ IDEA中的神仙插件 写代码必备,别说你还不知道
  3. Yii常用错误处理方法
  4. Android 中arm64-v8a、armeabi-v7a、armeabi、x86简介~
  5. Linux 设置双网卡通信,外网网卡和内网网卡
  6. 关于实时TopN排名算法的思考
  7. linux局域网不同网段ip互通,linux环境中,两个不同网段的机器互通
  8. (浙江大学数据结构)PTA Complete Binary Search Tree (10 分)
  9. windows线程 事件CreateEvent、SetEvent、ResetEvent、CloseHandle
  10. 2006全球50大#8220;最牛#215;…