网站首页 > 技术文章 正文
本篇讲解Java设计模式中的状态模式,分为定义、模式应用前案例、结构、模式应用后案例、适用场景、模式可能存在的困惑和本质探讨7个部分。
定义
状态模式允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
在新的分类方式中,状态模式被划分至类属性和行为联动相关需求类别中,其应对的是原有类中多种状态及导致的不同行为的情形。
模式应用前案例
从定义可以看出,状态模式隐含了一个对象可以有多种状态,并且每种状态应该有自身的行为。
学过计算机的同学应该对TCP连接比较熟悉,互联网的底层通信协议主要就是基于TCP。大家应该也对TCP的三次握手有所耳闻。下面,我们就以TCP连接为例,看看未使用状态模式之前的代码实现。
public class TCPConnection {//TCP连接类
private String state;
public void setState(String state) {
this.state = state;
}
public void listen(){
if ("LISTEN".equals(this.state)) {// 只有当处于监听状态时才能接受连接
System.out.println("TCP connection is now accepting incoming connections");
}else {
System.out.println("TCP connection is not in listening mode, unable to accept incoming connections");
}
}
public void open() {
if ("ESTABLISHED".equals(this.state)) {
System.out.println("TCP connection is already established");
} else if ("LISTEN".equals(this.state)) {
System.out.println("TCP connection is in listening mode");
// 监听连接请求的逻辑...
} else if ("CLOSED".equals(this.state)) {
System.out.println("TCP connection has been closed");
}
}
public void close() {
if ("ESTABLISHED".equals(this.state)) {
System.out.println("Closing the established TCP connection");
// 关闭已建立连接的逻辑...
} else if ("LISTEN".equals(this.state)) {
System.out.println("Closing the listening mode of TCP connection");
// 关闭监听模式的逻辑...
} else if ("CLOSED".equals(this.state)){
System.out.println("The TCP Connection is already closed");
}
}
}
public class Client {//调用方代码
public static void main(String[] args) {
TCPConnection tcpConnection = new TCPConnection();
// 初始状态为 LISTEN
tcpConnection.setState("LISTEN");
// 执行监听操作
tcpConnection.listen();
// 改变状态为 ESTABLISHED
tcpConnection.setState("ESTABLISHED");
// 执行打开连接操作
tcpConnection.open();
// 改变状态为 CLOSED
tcpConnection.setState("CLOSED");
//执行关闭连接操作
tcpConnection.close();
}
}
上述代码主要存在两大问题。一是TCPConnection类中包含了各种状态及不同行为的代码,如果后续还需要增加或删除状态,不满足OCP开闭原则。
二是Client类与TCPConnection之间进行状态信息交互时,还需要知晓具体状态的名称以及状态对应的方法名称,对调用方不友好。
结构
状态模式的示例代码实现如下。
public class Context {
private State state;
// 初始化 Context 时设置初始状态
public Context(State state) {
this.state = state;
}
// 设置当前状态
public void setState(State state) {
this.state = state;
}
// 请求处理,委托给当前状态对象
public void request() {
state.handle(this);
}
}
public interface State {
void handle(Context context);
}
public class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("ConcreteStateA handling the request.");
// 在某种条件下,切换到 ConcreteStateB
context.setState(new ConcreteStateB());
}
}
public class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("ConcreteStateB handling the request.");
// 在某种条件下,切换到 ConcreteStateC
context.setState(new ConcreteStateC());
}
}
public class ConcreteStateC implements State {
@Override
public void handle(Context context) {
System.out.println("ConcreteStateC handling the request.");
// 在某种条件下,可以切换回 ConcreteStateA 或其他状态
// context.setState(new ConcreteStateA());
}
}
public class Client {
public static void main(String[] args) {
// 创建 Context 对象,并设置初始状态为 ConcreteStateA
Context context = new Context(new ConcreteStateA());
// 执行请求,状态对象会根据内部逻辑处理请求,并可能切换状态
context.request(); // 输出 "ConcreteStateA handling the request."
// 再次执行请求,由于状态已切换为 ConcreteStateB,因此行为也会改变
context.request(); // 输出 "ConcreteStateB handling the request."
// 再次执行请求,由于状态已切换为 ConcreteStateC,因此行为也会改变
context.request(); // 输出 "ConcreteStateC handling the request."
}
}
从状态模式的结构和示例代码中,状态使用一个家族类来实现。Context核心类与状态家族类的抽象或接口关联,这样后续增加或删除状态都不需要改变Context核心类。
此外,通过状态家族类这种实现方式,可以将不同状态对外的行为都进行统一,对于调用方更加友好。
模式应用后案例
上面TCP连接状态的案例,在使用状态模式后的代码实现如下:
首先,状态抽象成一个家族类实现,包括一个状态接口和三个状态的实现。
public interface ITCPState {//TCP状态接口
abstract void handle(TCPConnection connection);
}
public class TCPListenState implements ITCPState {//TCPListen状态类
@Override
public void handle(TCPConnection connection) {
System.out.println( "TP Connection is now accepting incoming connections");
String clientAddress = "192.168.0.1";
int clientPort = 12345;
System.out.println("Incoming connection request from: "+clientAddress +":"+clientPort);
}
}
public class TCPEstablishedState implements ITCPState {//TCPEstablished状态类
@Override
public void handle(TCPConnection connection) {
System.out.println("TCP connection is already established");
}
}
public class TCPClosedState implements ITCPState {//TCPClosed状态类
@Override
public void handle(TCPConnection connection) {
System.out.println("The Connection is already closed");
}
}
原来TCPConnection大杂烩类简化如下,其中组合了状态家族类中的顶层接口,代码实现如下。
public class TCPConnection {//Context上下文类
private ITCPState state;
public TCPConnection(ITCPState state) {
this.state = state;
}
public void setState(ITCPState state) {
this.state = state;
}
public void request() {
this.state.handle(this);
}
}
最后,调用方代码实现如下。
public class Client {//调用方代码
public static void main(String[] args) {
// 创建TCPConnection对象并设置初始状态为Closed
TCPConnection tcpConnection = new TCPConnection(new TCPClosedState());
// 变化状态为 LISTEN
tcpConnection.setState(new TCPListenState());
// 执行监听操作
tcpConnection.request();
// 改变状态为 ESTABLISHED
tcpConnection.setState(new TCPEstablishedState());
// 执行打开连接操作
tcpConnection.request();
// 改变状态为 CLOSED
tcpConnection.setState(new TCPClosedState());
//执行关闭连接操作
tcpConnection.request();
}
}
相比原有的实现代码,现在TCPConnection类不会再因为状态的增加、删除而需要一并变更。
其次,Client类不再需要记住交互的细节信息,并且可以通过统一的接口的进行交互。
适用场景
当一个对象在生命周期中会产生多种状态,并且不同的状态下会产生相应的行为时,就应该考虑使用状态模式。
模式可能存在的困惑
困惑1: Context意思上是上下文类,为什么设计模式中会取这样一个名称?
在23个设计模式中,只有解释器模式、策略模式(后面讲到)和状态模式中有Context类。在三个设计模式中,未使用设计模式之前,Context类都是一个大杂烩类,既包括状态也包括不同的行为。
在使用设计模式之后,发现部分或全部核心的行为逻辑都被挪出去,而原来Context类中主要剩下了状态信息,并且这些状态信息成为行为发挥作用时的上下文信息。
困惑2:状态模式与解释器模式的结构非常类似,两者之间有什么区别?
结构上确实很类似,但是细节上有不同。解释器模式的核心是一种状态可以对应多种行为,而状态模式的核心是不同的状态对应不同的行为。
本质
在面向对象程序中,可以认为类是由状态+行为构成的。状态和行为之间可能有不同的关系,比如不同的状态有相同的行为、不同的状态有不同的行为、相同的状态有不同的行为等。状态模式的本质就是提供了一种处理不同状态有不同行为的机制。
猜你喜欢
- 2024-09-23 长文解析|九类交互状态的自查与设计
- 2024-09-23 如何使用Axure做一份高质量的EMS能源管理系统原型?
- 2024-09-23 计算机机箱的结构通常包括以下几个部分: ? 外壳
- 2024-09-23 标准化的响应结构对于C#项目至关重要
- 2024-09-23 产品架构设计之产品实体设计一,二
- 2024-09-23 PostgreSQL技术内幕3:PG逻辑存储层级和物理存储结构
- 2024-09-23 09 打造无状态的存储实现随时切库的写入服务
- 2024-09-23 众合科技获得发明专利授权:“基于状态机的冗余架构设备资源管理方法”
- 2024-09-23 mysql索引数据结构(mysql中索引的数据结构)
- 2024-09-23 Gh-ost让MySQL在线表结构变更不再是难题
- 1514℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 571℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 512℃MySQL service启动脚本浅析(r12笔记第59天)
- 486℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 485℃启用MySQL查询缓存(mysql8.0查询缓存)
- 468℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 447℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 445℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (83)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)