前端开发入门到精通的在线学习网站

网站首页 > 资源文章 正文

一文彻底搞明白状态模式(状态模式的理解)

qiguaw 2024-09-04 17:16:19 资源文章 24 ℃ 0 评论

本篇讲解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:状态模式与解释器模式的结构非常类似,两者之间有什么区别?

结构上确实很类似,但是细节上有不同。解释器模式的核心是一种状态可以对应多种行为,而状态模式的核心是不同的状态对应不同的行为。

本质

在面向对象程序中,可以认为类是由状态+行为构成的。状态和行为之间可能有不同的关系,比如不同的状态有相同的行为、不同的状态有不同的行为、相同的状态有不同的行为等。状态模式的本质就是提供了一种处理不同状态有不同行为的机制。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表