文档章节

折腾Java设计模式之状态模式

大萌小路
 大萌小路
发布于 04/04 09:26
字数 1985
阅读 17
收藏 0

原文地址 折腾Java设计模式之状态模式

状态模式

在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。通俗点就是一个对象在内部状态发生改变时改变它的行为。

介绍

意图 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。

主要解决 对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

何时使用 代码中包含大量与对象状态有关的条件语句。

如何解决 将各种具体的状态类抽象出来。

关键代码 通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。

UML图

image.png

主要角色

1)Context(环境类):环境类拥有各种不同状态的对象,作为外部使用的接口,负责调用状态类接口。

2)State(抽象状态):抽象状态既可以为抽象类,也可以直接定义成接口。主要用于定义状态抽象方法,具体实现由子类负责。

3)ConcreteState(具体状态类):具体状态类为抽象状态的实现者,不同的状态类对应这不同的状态,其内部实现也不相同。环境类中使用不同状态的对象时,能实现不同的处理逻辑

应用实例

1、打篮球的时候运动员可以有正常状态、不正常状态和超常状态。

2、曾侯乙编钟中,'钟是抽象接口','钟A'等是具体状态,'曾侯乙编钟'是具体环境(Context)。

优点

1、封装了转换规则。

2、枚举可能的状态,在枚举状态之前需要确定状态种类。

3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。

4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点

1、状态模式的使用必然会增加系统类和对象的个数。

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

3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

使用场景

  • 行为随状态改变而改变的场景。

  • 条件、分支语句的代替者。

状态模式和策略模式的对比

现在我们知道,状态模式和策略模式的结构是相似的,但它们的意图不同。让我们重温一下它们的主要不同之处:

  1. 策略模式封装了一组相关算法,它允许Client在运行时使用可互换的行为;状态模式帮助一个类在不同的状态显示不同的行为。
  2. 状态模式封装了对象的状态,而策略模式封装算法或策略。因为状态是跟对象密切相关的,它不能被重用;而通过从Context中分离出策略或算法,我们可以重用它们。
  3. 在状态模式中,每个状态通过持有Context的引用,来实现状态转移;但是每个策略都不持有Context的引用,它们只是被Context使用。
  4. 策略实现可以作为参数传递给使用它的对象,例如Collections.sort(),它的参数包含一个Comparator策略。另一方面,状态是Context对象自己的一部分,随着时间的推移,Context对象从一个状态转移到另一个状态。
  5. 虽然它们都符合OCP原则,策略模式也符合SRP原则(单一职责原则),因为每个策略都封装自己的算法,且不依赖其他策略。一个策略的改变,并不会导致其他策略的变化。
  6. 另一个理论上的不同:策略模式定义了对象“怎么做”的部分。例如,排序对象怎么对数据排序。状态模式定义了对象“是什么”和“什么时候做”的部分。例如,对象处于什么状态,什么时候处在某个特定的状态。
  7. 状态模式中很好的定义了状态转移的次序;而策略模式并无此需要:Client可以自由的选择任何策略。
  8. 一些常见的策略模式的例子是封装算法,例如排序算法,加密算法或者压缩算法。如果你看到你的代码需要使用不同类型的相关算法,那么考虑使用策略模式吧。而识别何时使用状态模式是很简单的:如果你需要管理状态和状态转移,但不想使用大量嵌套的条件语句,那么就是它了。
  9. 最后但最重要的一个不同之处是,策略的改变由Client完成;而状态的改变,由Context或状态自己。

项目实例

跳转到我的源码地址

simple1包中主要是对风扇的开关状态进行转换,其实我们是把状态放在状态类中进行按照固定的逻辑转换,但是这种模式其实他不符合开闭原则,为什么了,因为一旦我们发生新增、修改或者删除状态的时候,就需要修改状态类中的状态转换。

public class Application {

    public static void main(String[] args) {
        Context context = new Context(new CloseLevelState());
        context.right();
        context.right();
        context.right();
        context.left();
        context.right();
        context.right();
    }
}

image-20181226103419685

抽象状态

public interface LevelState {

    /**
     * 左转
     *
     * @param context
     */
    void left(Context context);

    /**
     * 右转
     *
     * @param context
     */
    void right(Context context);

    /**
     * 当前档位
     * @return
     */
    String info();
}

具体档位状态,我只列了2个,其他的类似

@Slf4j
public class OneLevelState implements LevelState {

    @Override
    public void left(Context context) {
        LevelState levelState = new CloseLevelState();
        context.setLevelState(levelState);
        log.info("风扇左转到{}", levelState.info());
    }

    @Override
    public void right(Context context) {
        LevelState levelState = new TwoLevelState();
        context.setLevelState(levelState);
        log.info("风扇右转到{}", levelState.info());
    }

    @Override
    public String info() {
        return "1档";
    }
}
@Slf4j
public class CloseLevelState implements LevelState {

    @Override
    public void left(Context context) {
        LevelState levelState = new ForeLevelState();
        context.setLevelState(levelState);
        log.info("风扇左转到{}", levelState.info());
    }

    @Override
    public void right(Context context) {
        LevelState levelState = new OneLevelState();
        context.setLevelState(levelState);
        log.info("风扇右转到{}", levelState.info());
    }

    @Override
    public String info() {
        return "0档";
    }

}

真正的开关也就是上下文

@Data
@AllArgsConstructor
public class Context {

    private LevelState levelState;

    public void left() {
        levelState.left(this);
    }

    public void right() {
        levelState.right(this);
    }

    public String info() {
        return levelState.info();
    }
}

参考

Java中,状态模式和策略模式的区别

状态模式|菜鸟教程

欢迎关注

微信公众号

© 著作权归作者所有

大萌小路
粉丝 1
博文 21
码字总数 30936
作品 0
深圳
私信 提问
设计模式 2014-12-19

book: 阎宏《JAVA与模式》 架构设计栏目 http://blog.csdn.net/enterprise/column.html 概要: http://bbs.csdn.net/forums/Embeddeddriver 23种设计模式分别是: 1.单例模式 2.工厂方法模式...

jayronwang
2014/12/19
296
0
设计模式15——Template Method设计模式

Template Method模板方法设计模式定义一个操作中算法的骨架,将具体步骤的执行延迟到子类中实现。Java中的抽象类就是使用了模板方法设计模式。模板方法设计模式结构如下: 以文档处理为例,T...

小米米儿小
2014/01/24
222
0
Java语言编程学习之Lambda表达式设计和架构的原则[图]

Java语言编程学习之Lambda表达式设计和架构的原则[图]: 大家都知道,Lambda表达式是对Java语言的一点简单改进,在JDK标准类库中,运行它的方式各种各样。但是大多数的Java代码都不是由开发J...

原创小博客
2018/07/17
32
0
Java语言编程学习之Lambda表达式设计和架构的原则

大家都知道,Lambda表达式是对Java语言的一点简单改进,在JDK标准类库中,运行它的方式各种各样。但是大多数的Java代码都不是由开发JDK的程序猿写的,而是像我们这样的普通程序猿。 很多人都...

JAVA高级架构开发
2018/08/19
0
0
为什么java中用枚举实现单例模式会更好

枚举单例是java中使用枚举提供一个实例对象来实现单例模式的一种新方法,虽然单例模式在java中早已存在,但枚举单例实际上从java5引入枚举作为它的关键特性之后相对来说还是一个新的概念,这...

zhoujy
2013/06/01
8.8K
1

没有更多内容

加载失败,请刷新页面

加载更多

前端面试题汇总

一. HTML常见的兼容性 1.HTML5 标签在低版本浏览器不兼容 解决办法:使用html5shiv库,引入下列语句 <!--[if lte IE 8]> <script src="https://cdn.bootcss.com/html5shiv/r29/html5.js"></sc......

蓝小驴
25分钟前
5
0
OSChina 周四乱弹 —— 我气的脸都黑了!

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 小小编辑推荐《Red Battle》- 高橋李依 / 豊崎愛生 《Red Battle》- 高橋李依 / 豊崎愛生 手机党少年们想听歌,请使劲儿戳(这里) @丶Lion ...

小小编辑
38分钟前
451
19
找OSG教程, B站就有

https://www.bilibili.com/video/av64849038?from=search&seid=11632913960900279653

洛克人杰洛
今天
5
0
学习记录(day07-Vue组件、自定义属性、自定义事件)

[TOC] 1.1.1什么是组件 一个vue文件就是一个组件 组件将html标签/css样式/对应JS打包成一个整体,也可以理解钻进一个具有样式和特效的自定义标签。 一、编写组件(提供方)<template> <di...

庭前云落
今天
5
0
使用Prometheus监控SpringBoot应用

通过之前的文章我们使用Prometheus监控了应用服务器node_exporter,数据库mysqld_exporter,今天我们来监控一下你的应用。(本文以SpringBoot 2.1.9.RELEASE 作为监控目标) 编码 添加依赖 使...

JAVA日知录
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部