【再谈设计模式】状态模式~对象行为的状态驱动者

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
实时计算 Flink 版,5000CU*H 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
简介: 状态模式属于行为型设计模式。它将对象的行为封装在不同的状态类中,使得对象在不同的状态下表现出不同的行为。上下文(Context):这是一个包含状态对象的类,它定义了客户感兴趣的接口,并维护一个具体状态对象的引用。上下文将操作委托给当前的状态对象来处理。抽象状态(State):这是一个抽象类或者接口,它定义了一个特定状态下的行为接口。所有具体的状态类都实现这个接口。具体状态(Concrete State):这些是实现抽象状态接口的类,每个具体状态类实现了与该状态相关的行为。

{B664E87D-286A-4B31-808A-15F47E3C470F}.png

一、引言

在软件开发,软件设计过程中,我们常常会遇到对象的行为依赖于其状态的情况。例如,一个任务对象可能有“未开始”、“进行中”、“已完成”等状态,并且在不同状态下执行相同操作会有不同的结果。传统的方法可能会使用大量的条件判断语句来处理不同状态下的行为,这使得代码复杂、难以维护且不易扩展。而状态模式提供了一种优雅的解决方案,它允许对象在内部状态改变时改变其行为,看起来就像是修改了类本身。

二、定义与描述

状态模式属于行为型设计模式。它将对象的行为封装在不同的状态类中,使得对象在不同的状态下表现出不同的行为。在状态模式中,有以下几个主要角色:
上下文(Context):这是一个包含状态对象的类,它定义了客户感兴趣的接口,并维护一个具体状态对象的引用。上下文将操作委托给当前的状态对象来处理。
抽象状态(State):这是一个抽象类或者接口,它定义了一个特定状态下的行为接口。所有具体的状态类都实现这个接口。
具体状态(Concrete State):这些是实现抽象状态接口的类,每个具体状态类实现了与该状态相关的行为。

三、抽象背景

考虑一个简单的订单处理系统。订单有不同的状态,如“已下单”、“已付款”、“已发货”、“已完成”等。在传统的设计中,如果要处理订单在不同状态下的操作(如显示订单状态、执行下一个流程等),可能会有如下的代码:
public class Order {
       private String state;    public Order(String state) {
           this.state = state;
    }    public void process() {
           if ("已下单".equals(state)) {
   
            System.out.println("订单已下单,等待付款");
        } else if ("已付款".equals(state)) {
   
            System.out.println("订单已付款,准备发货");
        } else if ("已发货".equals(state)) {
   
            System.out.println("订单已发货,等待收货");
        } else if ("已完成".equals(state)) {
   
            System.out.println("订单已完成");
        }
    }
}
    这种代码随着状态的增加和操作的复杂化会变得非常难以维护。而使用状态模式可以将每个状态的行为封装到独立的类中,使得代码结构更加清晰。

四、适用场景与现实问题解决

(一)适用场景

​    ​对象行为依赖于状态:当一个对象的行为根据其内部状态而改变时,状态模式非常合适。例如,游戏角色在不同的健康状态(满血、受伤、濒死)下有不同的行为(正常移动、缓慢移动、无法移动)。
​    ​避免大量条件判断:如果在代码中有大量的基于对象状态的条件判断语句,使用状态模式可以将这些判断分散到各个状态类中,提高代码的可读性和可维护性。

(二)现实问题解决

    以自动售货机为例,它有“空闲”、“投币”、“出货”等状态。如果使用传统的条件判断来处理每个状态下的操作(如投币操作在不同状态下的不同行为),代码会变得复杂且容易出错。而采用状态模式,可以清晰地定义每个状态下的行为,并且当需要添加新的状态或者修改现有状态的行为时,只需要在相应的状态类中进行修改,而不会影响到其他部分的代码。

{FE382273-E880-4B3A-8736-B7C2FDB6861A}.png

五、状态模式的现实生活的例子

    交通信号灯就是一个很好的状态模式的例子。交通信号灯有红、黄、绿三种状态,在不同的状态下,车辆和行人有不同的行为。例如,红灯时车辆停止,绿灯时车辆通行,黄灯时车辆减速准备停车。每个状态都有其对应的行为规则,并且状态之间可以按照一定的顺序转换。

{A1CD7739-1DB7-4C40-9B6B-C6EE392708AC}.png

六、初衷与问题解决

    初衷是为了更好地处理对象状态相关的行为,使得代码结构更加清晰、易于维护和扩展。通过将状态相关的行为封装到独立的状态类中,解决了传统方式中大量条件判断带来的代码复杂、不易理解和修改困难的问题。

七、代码示例

(一)Java示例

{C3AF4088-1590-4CBB-8F3C-D8B6FED8955E}.png
抽象状态接口

interface State {
       void handle(Context context);
}
    上下文类
class Context {
       private State state;    public Context(State state) {
           this.state = state;
    }    public void setState(State state) {
           this.state = state;
    }    public void request() {
   
        state.handle(this);
    }
}
    具体状态类示例(以两种状态为例)
class ConcreteStateA implements State {
       @Override
    public void handle(Context context) {
   
        System.out.println("ConcreteStateA处理请求");        // 状态转换示例
        context.setState(new ConcreteStateB());
    }
}class ConcreteStateB implements State {
       @Override
    public void handle(Context context) {
   
        System.out.println("ConcreteStateB处理请求");        // 状态转换示例
        context.setState(new ConcreteStateA());
    }
}
![{
   44CA60C6-30C9-41C5-9116-82AB4304B8F0}.png](https://ucc.alicdn.com/pic/developer-ecology/h6pyagd6fll2i_9b697bac86b34394ba9c91d50653527b.png)
    测试代码

{D2F534D7-30E7-4740-BF4A-629234E6E2BA}.png

public class Main {
       public static void main(String[] args) {
           Context context = new Context(new ConcreteStateA());
        context.request();
        context.request();
    }
}

(二)C++示例

    抽象状态类
class State {
   public:    virtual void handle(Context* context) = 0;
};
    上下文类
class Context {
   private:
    State* state;public:    Context(State* state) : state(state) {
   }    void setState(State* state) {
           this.state = state;
    }    void request() {
   
        state->handle(this);
    }
};
    具体状态类示例(以两种状态为例)
class ConcreteStateA : public State {
   public:    void handle(Context* context) override {
   
        std::cout << "ConcreteStateA处理请求" << std::endl;        // 状态转换示例
        context->setState(new ConcreteStateB());
    }
};class ConcreteStateB : public State {
   public:    void handle(Context* context) override {
   
        std::cout << "ConcreteStateB处理请求" << std::endl;        // 状态转换示例
        context->setState(new ConcreteStateA());
    }
};
    测试代码
int main() {
   
    Context* context = new Context(new ConcreteStateA());
    context->request();
    context->request();    return 0;
}

(三)Python示例

    抽象状态类(使用抽象基类实现)
from abc import ABC, abstractmethodclass State(ABC):   
   @abstractmethod def handle(self, context):       
                       pass
    上下文类
class Context:    def __init__(self, state):
        self.state = state    def setState(self, state):
        self.state = state    def request(self):
        self.state.handle(self)
    具体状态类示例(以两种状态为例)
class ConcreteStateA(State):    def handle(self, context):        print("ConcreteStateA处理请求")        # 状态转换示例
        context.setState(ConcreteStateB())class ConcreteStateB(State):    def handle(self, context):        print("ConcreteStateB处理请求")
   # 状态转换示例
 ```js

context.setState(ConcreteStateA())


        测试代码

```js
if __name__ == "__main__":
    context = Context(ConcreteStateA())
    context.request();
    context.request();

(四)Go示例

    抽象状态接口
type State interface {
   
    handle(context *Context)
}
    上下文结构体
type Context struct {
   
    state State
}func (c *Context) setState(state State) {
   
    c.state = state
}func (c *Context) request() {
   
    c.state.handle(c)
}
    具体状态结构体示例(以两种状态为例)
type ConcreteStateA struct{
   }func (c ConcreteStateA) handle(context *Context) {
       println("ConcreteStateA处理请求")    // 状态转换示例
    context.setState(ConcreteStateB{
   })
}type ConcreteStateB struct{
   }func (c ConcreteStateB) handle(context *Context) {
       println("ConcreteStateB处理请求")    // 状态转换示例
    context.setState(ConcreteStateA{
   })
}
    测试代码
func main() {
   
    context := &Context{
   state: ConcreteStateA{
   }}
    context.request()
    context.request()
}

八、状态模式的优缺点

(一)优点

​    ​提高代码的可维护性:将每个状态的行为封装到独立的类中,使得代码结构清晰,易于理解和修改。当需要添加新的状态或者修改现有状态的行为时,只需要在相应的状态类中进行操作,而不会影响到其他部分的代码。
​    ​符合开闭原则:对扩展开放,对修改关闭。可以轻松地添加新的状态类而不需要修改原有的上下文类和其他状态类的代码。
​    ​增强代码的可读性:避免了大量的条件判断语句,使得代码逻辑更加直观。

(二)缺点

​    ​类的数量增加:每个状态都需要一个对应的状态类,这可能会导致类的数量过多,特别是在状态比较复杂或者状态数量较多的情况下。
​    ​状态转换逻辑分散:状态之间的转换逻辑分布在各个状态类中,如果状态转换比较复杂,可能会导致代码的理解和维护成本增加。

{EAC0443C-9C7B-4739-868F-B6D9CFC40C86}.png

九、状态模式的升级版

    一种常见的升级版是引入状态机框架。状态机框架可以更加方便地管理状态之间的转换关系,提供了统一的状态转换规则配置方式。例如,在一些游戏开发中使用的有限状态机(FSM)框架,它可以预先定义好所有的状态和状态之间的转换条件,然后在游戏运行过程中根据实际情况进行状态转换。这样不仅提高了状态管理的效率,还使得整个状态处理流程更加清晰、可控。

{08E44E43-5F50-46A3-B537-6ED41D785F89}.png

目录
相关文章
|
21天前
|
设计模式 存储 Java
【再谈设计模式】备忘录模式~对象状态的守护者
备忘录模式属于行为型设计模式。它的主要目的是在不破坏对象封装性的前提下,捕获并外部化一个对象的内部状态,以便之后可以将该对象恢复到这个状态。原发器(Originator):创建一个备忘录,用于记录当前时刻它的内部状态。原发器还可以使用备忘录来恢复其内部状态。备忘录(Memento):存储原发器对象的内部状态。备忘录应该防止原发器以外的其他对象访问其内部状态。负责人(Caretaker):负责保存备忘录,但不能对备忘录的内容进行操作或检查。
185 82
|
1月前
|
设计模式 供应链 安全
【再谈设计模式】中介者模式 - 协调对象间交互的枢纽
中介者模式定义了一个中介对象来封装一组对象之间的交互方式。中介者使得各对象之间不需要显式地相互引用,从而降低了它们之间的耦合度。它通过将对象之间的交互逻辑集中到中介者对象中,使得系统的结构更加清晰,易于维护和扩展。
56 18
【再谈设计模式】中介者模式 - 协调对象间交互的枢纽
|
27天前
|
设计模式 算法 Java
【再谈设计模式】访问者模式~操作对象结构的新视角
  访问者模式是一种行为设计模式,旨在解决对象结构与操作逻辑的耦合问题。在软件系统开发中,当面临复杂的对象结构(如多种类型对象组成的树形或图形结构),且需要对这些对象执行不同操作时,传统方式将操作直接写在对象类中会导致类职责过多,不利于维护和扩展。而访问者模式通过将操作与对象结构分离,允许在不改变现有对象结构的情况下定义新操作,元素接受访问者访问,访问者定义对不同类型元素的操作逻辑,从而为应对这种复杂情况提供了有效的解决方案。
28 0
|
6月前
|
设计模式 Java 测试技术
Java设计模式-状态模式(18)
Java设计模式-状态模式(18)
|
7月前
|
设计模式 网络协议 Java
【十五】设计模式~~~行为型模式~~~状态模式(Java)
文章详细介绍了状态模式(State Pattern),这是一种对象行为型模式,用于处理对象在其内部状态改变时的行为变化。文中通过案例分析,如银行账户状态管理和屏幕放大镜工具,展示了状态模式的应用场景和设计方法。文章阐述了状态模式的动机、定义、结构、优点、缺点以及适用情况,并提供了Java代码实现和测试结果。状态模式通过将对象的状态和行为封装在独立的状态类中,提高了系统的可扩展性和可维护性。
【十五】设计模式~~~行为型模式~~~状态模式(Java)
|
7月前
|
设计模式 存储 安全
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
163 0
|
3月前
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
197 11
|
4月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
18天前
|
设计模式 Java 关系型数据库
设计模式:工厂方法模式(Factory Method)
工厂方法模式是一种创建型设计模式,通过将对象的创建延迟到子类实现解耦。其核心是抽象工厂声明工厂方法返回抽象产品,具体工厂重写该方法返回具体产品实例。适用于动态扩展产品类型、复杂创建逻辑和框架设计等场景,如日志记录器、数据库连接池等。优点包括符合开闭原则、解耦客户端与具体产品;缺点是可能增加类数量和复杂度。典型应用如Java集合框架、Spring BeanFactory等。
|
2月前
|
设计模式
「全网最细 + 实战源码案例」设计模式——模式扩展(配置工厂)
该设计通过配置文件和反射机制动态选择具体工厂,减少硬编码依赖,提升系统灵活性和扩展性。配置文件解耦、反射创建对象,新增产品族无需修改客户端代码。示例中,`CoffeeFactory`类加载配置文件并使用反射生成咖啡对象,客户端调用时只需指定名称即可获取对应产品实例。
95 40