前言

状态模式是一种行为模式,用于解决系统中复杂的对象状态转换以及各个状态下的封装等问题。状态模式是将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象的状态可以灵活多变。这样在客户端使用时无需关心对象的状态,可以实现自身的一致性处理。最近工作有些忙,更新博客慢了。还是要严格要求自己的,抽时间也要坚持学习。

状态模式

概念介绍

状态模式允许一个对象在其状态改变时,改变它的行为,对象看起来似乎修改了它的类。

想要在改变自身状态时改变对象行为,最直接的方法就是在代码中将所有可能发生的情况都考虑到了,然后使用if-else语句来进行相应的选择。但是这种方法对于复杂的状态判断会显得杂乱无章,容易产生错误;而且增加一个新的状态将会带来大量的修改。

流程结构如下图:

此时“能够修改自身”的状态模式的引入也许是个不错的主意,将不同条件下的行为封装在一个类里,再给这些类一个统一的父类来约束它们。

就会变成如下图流程结构:

举例

还是来举个具体的实例来说明一下吧。同事小王要请假,根据公司规定,请假要先在OA上提请假申请单,然后审批通过后就可以正常休假了。这个请假申请单大致经历这么几个状态,未审核(待提交)、审核中、审核通过、审核未通过,这里只是粗略的分为这几个状态。下面我们使用状态模式来模拟实现一下这个请假申请单的状态流转过程。

先创建一个休假申请单类

/**
* 休假申请
*/
public class LeaveApply {
/**
* 休假申请单初始状态是待提交状态
*/
private ApplyState applyState = new UnAudited(); /**
* 设置状态
* @param state
*/
public void setState(ApplyState state){
applyState = state;
} /**
* 状态变化后,更新对象自身的行为
*/
public void update(){
applyState.changeHandle();
} }

审批单状态接口,声明统一处理的方法。

/**
* 审批单状态接口
*/
public interface ApplyState { /**
* 状态变化处理操作
*/
void changeHandle(); }

待提交状态

/**
* 未审核状态(待提交审核)
*/
public class UnAudited implements ApplyState {
/**
* 状态变化处理操作
*/
@Override
public void changeHandle() {
System.out.println("申请单处于未审核状态,当用户查看申请单详情时直接跳转到编辑页。");
}
}

审核中状态

/**
* 审核中状态
*/
public class Audit implements ApplyState {
/**
* 状态变化处理操作
*/
@Override
public void changeHandle() {
System.out.println("申请单处于审核中状态,当用户查看申请单详情时跳转到详情页可以看到提交记录。");
}
}

审核通过状态

/**
*
* 审核通过状态
*/
public class Pass implements ApplyState {
/**
* 状态变化处理操作
*/
@Override
public void changeHandle() {
System.out.println("申请单已经审批通过,当前用户可以正常休假了。");
}
}

审核未通过状态

/**
* 审核未通过状态
*/
public class NotPass implements ApplyState {
/**
* 状态变化处理操作
*/
@Override
public void changeHandle() {
System.out.println("申请单未通过审核,当前用户不可以休假");
}
}

测试类

public class TestOA {

    public static void main(String[] args) {

        //创建一个请假申请单
LeaveApply leaveApply = new LeaveApply(); leaveApply.setState(new UnAudited());
leaveApply.update(); leaveApply.setState(new Audit());
leaveApply.update(); leaveApply.setState(new Pass());
leaveApply.update(); leaveApply.setState(new NotPass());
leaveApply.update(); }
}

运行结果:

申请单处于未审核状态,当用户查看申请单详情时直接跳转到编辑页。
申请单处于审核中状态,当用户查看申请单详情时跳转到详情页可以看到提交记录。
申请单已经审批通过,当前用户可以正常休假了。
申请单未通过审核,当前用户不可以休假

这个例子如果是不使用状态模式的思想,而是使用条件语句来实现就会出现很多的if-else来进行判断什么状态,应该执行什么样的方法。现在把各个状态的处理逻辑分离,结构清晰了并且耦合也不那么紧密了。

结构分析

在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心。状态模式的结构组成如下图:

在状态模式中,主要涉及了如下几个角色。

环境角色(Context):定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。

抽象状态角色(State):定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。

具体状态角色(Contract):每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。

总结

状态模式在功能上和策略模式很类似,但是在实现思想上状态模式是将各个状态分离解耦的,并且可以将对象的具体行为委托给当前的状态对象,而策略模式中,策略的选择是根据Context类中的行为来确定的,也不存在各个状态的切换。在实际开发中,状态模式具有较高的使用频率,在工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。

主要优点

1、封装了状态的转换规则,在状态模式中可以将状态转换的工作封装在环境类或具体的状态类中,可以对状态转换码进行集中管理,而不是分散在一个个的业务中。

2、将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。

3、允许状态转换逻辑与状态对象合为一体,而不是提供一个巨大的条件语句块,状态模式可以让我们避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起。

主要缺点

1、状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。

2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。

使用场景

1、对象的行为依赖于它的状态(如某些属性值),状态的改变将导致行为的变化。

2、在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强。

想了解更多的设计模式请查看Java设计模式学习记录-GoF设计模式概述

这个是我的个人公众号,文章以后也会同步到公众号上去,欢迎关注。

Java设计模式学习记录-状态模式的更多相关文章

  1. Java设计模式学习记录-备忘录模式

    前言 这次要介绍的是备忘录模式,也是行为模式的一种 .现在人们的智能手机上都会有备忘录这样一个功能,大家也都会用,就是为了记住某件事情,防止以后自己忘记了.那么备忘录模式又是什么样子的呢?是不是和手机 ...

  2. Java设计模式学习记录-模板方法模式

    前言 模板方法模式,定义一个操作中算法的骨架,而将一些步骤延迟到子类中.使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤. 模板方法模式 概念介绍 模板方法模式,其实是很好理解的,具体 ...

  3. Java设计模式学习记录-迭代器模式

    前言 这次要介绍的是迭代器模式,也是一种行为模式.我现在觉得写博客有点应付了,前阵子一天一篇,感觉这样其实有点没理解透彻就写下来了,而且写完后自己也没有多看几遍,上次在面试的时候被问到java中的I/ ...

  4. Java设计模式学习记录-解释器模式

    前言 这次介绍另一个行为模式,解释器模式,都说解释器模式用的少,其实只是我们在日常的开发中用的少,但是一些开源框架中还是能见到它的影子,例如:spring的spEL表达式在解析时就用到了解释器模式,以 ...

  5. Java设计模式学习记录-命令模式

    前言 这次要介绍的是命令模式,这也是一种行为型模式.最近反正没有面试机会我就写博客呗,该投的简历都投了.然后就继续看书,其实看书也会给自己带来成就感,原来以前不明白的东西,书上已经给彻底的介绍清楚了, ...

  6. Java设计模式学习记录-外观模式

    前言 这次要介绍的是外观模式(也称为门面模式),外观模式也属于结构型模式,其实外观模式还是非常好理解的,简单的来讲就是将多个复杂的业务封装成一个方法,在调用此方法时可以不必关系具体执行了哪些业务,而只 ...

  7. Java设计模式学习记录-桥接模式

    前言 这次介绍结构型设计模式中的第二种模式,桥接模式. 使用桥接模式的目的就是为了解耦,松散的耦合更利于扩展,但是会增加相应的代码量和设计难度. 桥接模式 桥接模式是为了将抽象化与实现化解耦,让二者可 ...

  8. Java设计模式学习记录-代理模式

    代理模式 代理模式是常见设计模式的一种,代理模式的定义是:为其他对象提供一种代理以控制对这个对象的访问. 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起 ...

  9. Java设计模式学习记录-建造者模式

    前言 今天周末,有小雨,正好也不用出门了,那就在家学习吧,经过了两周的面试,拿到了几个offer,但是都不是自己很想去的那种,要么就是几个人的初创小公司,要么就是开发企业内部系统的这种传统开发,感觉这 ...

随机推荐

  1. springboot整合redis-sentinel支持Cache注解

    一.前提 已经存在一个redis-sentinel集群,两个哨兵分别如下: /home/redis-sentinel-cluster/sentinel-1.conf port 26379 dir &q ...

  2. unic

    在线考试 答题剩余时间0小时51分18秒 考生须知 1.本次考试结束后,剩余补考次数:2次 2.考试时间为60分钟,超时系统自动交卷 3.本次考试满分100分(5*20道),60分通过考试 1. (单 ...

  3. scrapy的入门使用(一)

    1. scrapy项目实现流程 创建一个scrapy项目:scrapy startproject mySpider 生成一个爬虫:scrapy genspider  提取数据:完善spider,使用x ...

  4. 怎样用git上传代码到github以及如何更新代码

    上传代码: 1.进入指定文件夹: cd 指定文件夹 2.初始化git仓库: git init 3.将项目所有文件添加到暂存区: git add . 4.提交到仓库: git commit -m &qu ...

  5. C#实现全局快捷键(系统热键)响应(转)

    转自http://www.cnblogs.com/Randy0528/archive/2013/02/04/2892062.html 在应用中,我们可能会需要实现像Ctrl+C复制.Ctrl+V粘贴这 ...

  6. JavaGC学习笔记

    1.简介Java在JVM虚拟机上的垃圾回收(GC)机制,在合适的时间触发垃圾回收,将不需要的内存空间回收释放,避免无限制的内存增长导致的OOM. 1.1 Java堆内存结构Java将堆内存分为3大部分 ...

  7. [转载]ECMall模板解析语法与机制

    ECMall模板解析语法与机制 2011-05-22 在ECMall模板中,用"{"开头,以"}"结尾就构成一个标签单元,"{"紧接着的单词 ...

  8. php中ob_get_contents、curl_multi_init、curl_init多线程下载远程图片并保存记录

    php中三种方式测试图片下载效率 原文共24张不同图,每张大小在500K以上 使用时注意调整传入数组格式以及需要下载时保存地址的路径格式等 这三种方式无需额外安装扩展,方便快捷易操作[虽然效率看结果没 ...

  9. js读取txt文件

    $('#selectAreaFile').on('change',function(e){//用户区域的文件选择        var files=e.target.files;        if ...

  10. 6-使用requests库封装类处理get/post请求

    1.request安装 1)pip安装,直接pip install requests 2)下载离线包安装,加压后,命令行进入路径,执行python setup.py install 2.创建工程 注意 ...