设计模式详解——状态模式
前言
今天我们来看一个号称策略模式双胞胎的设计模式——状态模式,如它的名字一样,状态模式最核心的设计思路就是将对象的状态抽象出一个接口,然后根据它的不同状态封装其行为,这样就可以实现状态和行为的绑定,最终实现对象和状态的有效解耦。下面我们就来详细看下它的基本原理和实现过程吧。
状态模式
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
要点
- 状态模式允许一个对象基于内部状态而拥有不同的行为
- 和程序状态机(
PSM
)不同,状态模式用类代表状态 Context
会将行为委托给当前状态对象- 通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了
- 状态模式和策略模式有相同的类图,但是它们的意图不同
- 策略模式通常会用行为或算法来配置
Context
类 - 状态模式允许
Context
随着状态的改变而改变行为 - 状态转换可以由
State
类或Context
类控制 - 使用状态模式通常会导致设计中类的数目大量增加
- 状态类可以被多个
Context
示例共享
优缺点
优点
-
封装了转换规则。
-
枚举可能的状态,在枚举状态之前需要确定状态种类。
-
将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
-
允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块
-
可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点
- 状态模式的使用必然会增加系统类和对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
- 状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景
- 行为随状态改变而改变的场景。
- 条件、分支语句的代替者。
示例
状态接口
首先是状态接口,这个接口是给我们实际的状态对象继承的,这个接口有一个方法doAction
,这个方法就是给不同的状态对象实现的,用于处理不同状态下的行为的。
public interface State {
/**
* 改变状态的操作
* @param context
*/
void doAction(Context context);
}
状态所属者
然后是我们的状态所属者,这个类有一个核心的属性就是我们的State
接口。
public class Context {
private State state;
public Context(){}
public void setState(State state){
this.state = state;
}
public State getState(){
return state;
}
@Override
public String toString() {
return "Context{" +
"state=" + state +
'}';
}
}
状态实现
状态实现者继承了State
接口,并实现了doAction
方法,在方法内部可以对我们的状态所有者进行对应的操作。
这里是一个启动状态:
public class StopState implements State {
private String name;
public StopState() {
this.name = "stop";
}
@Override
public void doAction(Context context) {
System.out.println("Context is in stop state");
context.setState(this);
System.out.println(context);
}
@Override
public String toString() {
return "StopState{" +
"name='" + name + '\'' +
'}';
}
}
这里是停止状态
public class StartState implements State{
private String name;
public StartState() {
this.name = "start";
}
@Override
public void doAction(Context context) {
System.out.println("Context is in start state");
context.setState(this);
System.out.println(context);
}
@Override
public String toString() {
return "StartState{" +
"name='" + name + '\'' +
'}';
}
}
测试代码
这里分别实例化了容器和状态的示例,然后通过示例的doAction
方法操作容器
@Test
public void testState() {
Context context = new Context();
StartState startState = new StartState();
startState.doAction(context);
StopState stopState = new StopState();
stopState.doAction(context);
}
运行结果
可以看到,状态对象的doAction
方法执行后,容器对应的状态也发生了改变:
好了,关于状态模式就先说这么多,接下来我们做一个简单的总结。
总结
有用过策略模式或者对策略模式比较熟悉的小伙伴应该发现了:策略模式其实和我们今天分析状态模式特别像,甚至连架构模式都是一样的,所以这里我们有必要说下它们的区别。
首先是策略模式,它其实是将不同的算法封装成不同的策略,然后在具体的策略中实现具体的行为,但是测试本身是被动被选择的,容器选择策略,调用过程发生在容器中,而且策略本身是入参;
而我们今天分析的状态模式,它是将不同状态对应的行为封装,然后由具体的状态操作容器,整个过程更像是状态主动发起的,由状态执行其自己的方法,入参是容器。
这两种设计模式从某种程度上说是可以互相替换的,但是还是要结合具体业务分析的,比如spring boot
启动过程中,它用到的就是状态模式,这一点我们在分析spring boot
启动过程中也发现了;但如果是涉及到算法层面的内容,比如两个数的加减乘除,显然策略模式才是更好的选择。
总之,学习设计模式除了要了解它的基本原理和应用场景之外,更重要的是,要学会辨识优秀框架中的设计模式(知识,知就是知道,了解,识局势辨识,分析),最终将这些设计模式应用到我们的业务开发之中。
原文地址:https://www.cnblogs.com/caoleiCoding/p/15449802.html
- 利用Geneva开发SOA的安全模型
- STOMP协议介绍
- ADO.NET实体框架连接串引发的异常:Unable to load the specified metadata resource
- Mono产品生命周期
- WordPress免插件仅代码实现文章归档模板 II
- Paket 介绍
- C语言学不会,编程能力无法提升?你的问题我来解决!
- 实现WCF和Unity 的集成
- Qt中纯C++项目发布为dll的方法(超详细步骤)
- .NET的Actor模型:Orleans
- UML:类图复习-鸡生蛋,蛋生鸡
- DotNet多个程序集合并工具
- Spring官网下载dist.zip的几种方法
- Spring Security笔记:HTTP Basic 认证
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- single-spa 基础概念
- DVWA-对Command Injection(命令注入)的简单演示与分析
- 如何探测内网存活主机
- Java ServletContext详解
- Java web Cookie详解(持久化+原理详解+共享问题+设置中文+发送多个Cookie)
- 实战记录之SQL server报错手工注入
- 树莓派基础实验23:触摸开关传感器实验
- springmvc 文件下载 VS resteasy 文件上传下载
- Struts Scan工具的使用
- 树莓派基础实验24:超声波测距传感器实验
- 数据结构与算法系列2 线性表 链表的分类+使用java实现链表+链表源码详解
- 从spring boot 启动过程看apollo的初始化过程( 二)
- Apache IoTDB 系列教程-4:客户端接口
- 数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解
- Kali中密码暴力破解工具hydra的使用