【设计模式系列笔记】状态模式

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 Tair(兼容Redis),内存型 2GB
简介: 在Java中,状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。状态模式的关键思想是将对象的状态封装成独立的类,并将对象的行为委托给当前状态的对象。这样,当对象的状态发生变化时,其行为也会相应地发生变化。

1. 状态模式的介绍

在Java中,状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。状态模式的关键思想是将对象的状态封装成独立的类,并将对象的行为委托给当前状态的对象。这样,当对象的状态发生变化时,其行为也会相应地发生变化。

状态模式它帮助对象在不同状态下改变其行为,而无需改变对象本身的代码。我们可以将对象的状态抽象成独立的类,然后通过切换这些状态类,实现对象行为的动态改变。

比如,想象一个自动售货机,它可以处于不同的状态,比如“有货物”的状态和“售罄”的状态。在状态模式中,我们可以创建两个状态类,一个表示有货物的状态,另一个表示售罄的状态。售货机本身拥有一个当前状态的引用,并根据这个状态执行不同的行为,比如投币、选择商品等。

通过状态模式,我们可以轻松地添加新的状态,而不必修改售货机的核心代码。这使得系统更加灵活,易于维护和扩展。

简而言之,状态模式就是让对象在不同状态下表现出不同的行为,而且能够方便地切换状态,使得代码更加清晰和可扩展。

2. 关键思想

状态模式的关键思想是将对象的状态封装成独立的类,并使得对象在不同状态下能够切换。这样,当对象的状态发生变化时,其行为也会相应地发生变化,而不必改变对象本身的代码。以下是状态模式的核心思想:

  1. 将每个状态封装成类: 每个状态都被抽象为一个独立的类,这些类实现一个共同的接口或继承自一个共同的抽象类,该接口或抽象类定义了在该状态下对象可能执行的行为。
  2. 将对象的状态委托给状态类: 对象持有一个当前状态的引用,并将与状态相关的行为委托给当前状态对象执行。这样,对象的行为可以随着状态的变化而变化,而且状态的切换对客户端是透明的。
  3. 允许状态动态切换: 对象可以在运行时动态地改变其状态,从而改变其行为。这种动态切换状态的能力使得对象更加灵活,能够适应不同的场景和需求。

通过这种方式,状态模式实现了对象行为和状态的解耦,使得系统更容易理解、扩展和维护。这种模式特别适用于对象有多个状态且状态之间的转换比较复杂的情况。

3. 实现方式

状态模式的实现方式主要包括以下几个关键元素:状态接口、具体状态类、上下文类,以及客户端代码。

  1. 状态接口(State Interface): 定义状态类所需的方法,这些方法描述了对象在该状态下应该执行的行为。
// State 接口是状态模式的关键组成部分,用于定义各个具体状态类所需实现的方法。
public interface State {
    // handle 方法用于描述对象在当前状态下应该执行的行为。
    void handle();
}

这个接口是状态模式的基础,它规定了具体状态类应该提供的行为。在状态模式中,每个具体状态类都要实现这个接口,以便根据对象的当前状态执行相应的行为。这样,通过委托给具体状态类的方法,对象可以在运行时动态改变其行为。

  1. 具体状态类(Concrete State Classes): 实现状态接口,为对象在不同状态下定义具体的行为。
// ConcreteStateA 类是 State 接口的具体实现,表示对象处于状态A时的行为。
public class ConcreteStateA implements State {
    @Override
    public void handle() {
        // 在具体状态A下的行为
        System.out.println("处理状态 A");
    }
}
// ConcreteStateB 类是 State 接口的具体实现,表示对象处于状态B时的行为。
public class ConcreteStateB implements State {
    @Override
    public void handle() {
        // 在具体状态B下的行为
        System.out.println("处理状态 B");
    }
}

这两个类分别代表了对象可能的两种状态(A和B),并且实现了 State 接口的 handle 方法,该方法定义了对象在各自状态下应该执行的行为。在实际应用中,可以根据具体业务需求定义更多的具体状态类,每个类都负责描述对象在某一特定状态下的行为。在状态模式中,这种组织结构使得状态的变化不再需要修改上下文对象,从而实现了状态和行为的解耦。

  1. 上下文类(Context Class): 持有当前状态的引用,可以动态切换状态,并将状态相关的行为委托给当前状态对象执行。
// Context 类是状态模式中的上下文类,负责维护对象的当前状态,并在状态发生变化时调用当前状态的方法。
public class Context {
    private State currentState;
    // setState 方法用于设置当前状态,允许在运行时动态改变对象的状态。
    public void setState(State state) {
        this.currentState = state;
    }
    // request 方法用于触发对象的行为,实际上委托给当前状态对象的 handle 方法执行。
    public void request() {
        currentState.handle();
    }
}

Context 类充当了状态模式中的上下文角色,持有一个当前状态的引用,并提供了两个关键方法。setState 方法用于动态设置对象的当前状态,而 request 方法则触发对象的行为,实际上调用了当前状态对象的 handle 方法。通过这种方式,对象可以在运行时根据其状态的变化而改变行为,而且状态的切换对客户端是透明的。

  1. 客户端代码(Client): 创建上下文对象和具体状态对象,并在运行时切换状态。
// Client 类是状态模式的客户端,演示了如何使用状态模式创建上下文对象并在运行时切换状态。
public class Client {
    public static void main(String[] args) {
        // 创建上下文对象
        Context context = new Context();
        // 创建两个具体状态对象,分别表示状态A和状态B
        State stateA = new ConcreteStateA();
        State stateB = new ConcreteStateB();
        // 设置初始状态为状态A,并触发对象的行为
        context.setState(stateA);
        context.request(); // 处理状态A的行为
        // 将状态切换为状态B,并再次触发对象的行为
        context.setState(stateB);
        context.request(); // 处理状态B的行为
    }
}

Client 类演示了如何使用状态模式。首先,创建了一个上下文对象 context,然后创建了两个具体状态对象 stateAstateB,分别表示对象可能的两种状态。通过调用 context.setState() 方法,可以在运行时切换对象的状态,并通过 context.request() 方法触发对象的行为。在此示例中,演示了切换到状态A和状态B时,对象的行为分别由具体状态A和具体状态B定义。

通过这种实现方式,对象的状态和行为被解耦,状态的切换变得灵活,客户端代码可以轻松地添加新的状态和调整状态之间的转换关系,从而实现更清晰、可维护的代码结构。

要点:

  1. 状态与行为的分离: 状态模式通过将对象的状态封装成独立的类,实现了状态与行为的分离。这使得对象在不同状态下可以有不同的行为,而且状态的切换对客户端是透明的。
  2. 具体状态类的独立性: 每个具体状态类都是独立的,负责定义对象在特定状态下的行为。这种独立性使得增加新的状态类变得容易,而且不会影响到其他状态类和上下文类。
  3. 上下文类委托: 上下文类持有当前状态的引用,并将状态相关的行为委托给当前状态对象执行。这种委托机制使得状态的变化不需要修改上下文类的代码,实现了开闭原则。
  4. 动态切换状态: 状态模式允许对象在运行时动态地改变其状态,从而改变其行为。这种动态切换状态的能力使得系统更加灵活和可扩展。

注意事项:

  1. 合适的场景: 状态模式适用于对象具有多个状态且状态之间的转换较为复杂的情况。如果对象的状态很少,或者状态之间的转换很简单,可能并不适合使用状态模式。
  2. 避免滥用: 不是所有的状态切换都适合使用状态模式。在一些简单的场景中,状态模式可能会引入过多的类和复杂性,导致代码难以维护。在选择使用状态模式时,需要权衡其带来的优势和额外的复杂性。
  3. 状态间的转换逻辑: 状态模式并没有提供直接的机制来管理状态之间的转换逻辑,这通常由上下文类或其他协调者来负责。因此,在设计时需要考虑清楚状态之间的转换关系。
  4. 一致性: 各个具体状态类的行为应该保持一致性,即在同一状态下,不同的具体状态类应该实现相同的方法名,并保持一致的语义。这样可以确保在状态切换时不会出现行为的突然变化。

优点:

  1. 清晰的结构: 状态模式将对象的不同状态及其相应的行为分离成独立的类,使得系统结构清晰,各个状态的变化互相独立。
  2. 易于扩展: 新增一个状态类相对容易,无需修改现有状态类或上下文类的代码。每个状态类都可以独立变化,使得系统更容易扩展。
  3. 状态切换简化: 状态模式将状态切换的逻辑集中在状态类中,使得状态之间的转换逻辑更加清晰和易于理解。
  4. 符合开闭原则: 添加新的状态类不会修改已有的代码,而是扩展代码,符合开闭原则,使得系统更容易维护和扩展。

缺点:

  1. 类数量增加: 使用状态模式会增加系统中类的数量,每个状态都需要一个独立的类,可能导致类的数量过多,使得代码结构复杂。
  2. 状态切换逻辑集中: 虽然状态切换的逻辑被集中在状态类中,但有时可能导致上下文类中的代码变得复杂,尤其是状态转换较为复杂时。

应用场景:

  1. 对象有多个状态: 当对象在不同状态下有不同的行为,并且状态之间的转换比较复杂时,可以考虑使用状态模式。
  2. 行为随状态改变而改变: 如果对象的行为需要根据其内部状态动态变化,而且这些状态在运行时可以发生变化,状态模式是一个合适的选择。
  3. 避免大量的条件判断语句: 当有大量的条件判断语句用于确定对象的行为时,可以考虑使用状态模式来替代这些条件语句,使代码更加清晰。
  4. 状态之间共享少量代码: 如果多个状态下的行为有一些共享的代码,但又有一些不同的行为,状态模式可以将共享的代码放在上下文类中,避免代码重复。



目录
相关文章
|
8天前
|
设计模式 Java API
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
|
8天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
|
8天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
23天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
27 2
|
4天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
4天前
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
|
4天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
14 3
|
4天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
17 2
|
4天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
13 1
|
4天前
|
设计模式 Java API
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
11 1