设计模式——17. 状态模式

1. 说明

状态模式(State Pattern)是一种行为设计模式,它允许一个对象在其内部状态发生改变时改变其行为。状态模式将对象的状态封装成不同的状态对象,并将状态切换时的行为委托给当前状态对象。这样,对象在不同状态下具有不同的行为,而无需在对象本身中使用大量的条件语句来管理状态。

状态模式通常包括以下几个关键角色:

  1. Context(上下文):上下文是拥有状态的对象,它包含一个指向当前状态对象的引用。上下文可以根据需要在不同状态之间切换,并委托状态对象来执行特定的行为。
  2. State(状态):状态是表示不同状态的接口或抽象类,它定义了状态对象应该实现的方法。通常,每个具体状态都对应一个具体状态类,该类实现了状态接口。
  3. ConcreteState(具体状态):具体状态是状态的具体实现类,它实现了状态接口中定义的方法,以执行与状态相关的操作。

通过状态模式,对象的行为可以根据其内部状态的改变而改变,这使得代码更具可维护性、可扩展性和可读性。状态模式常用于处理对象在状态转换时的复杂行为,以及减少大量的条件分支语句。

2. 使用的场景

状态模式通常适用于以下情况和场景:

  1. 对象具有多种状态,且状态之间存在转换关系:当对象的行为随着其内部状态的变化而变化,并且这些状态之间存在复杂的转换逻辑时,状态模式非常有用。状态模式可以将每个状态封装成一个独立的类,简化状态之间的转换和管理。
  2. 对象的行为在不同状态下有不同的实现:如果对象在不同状态下执行相同操作的方式不同,状态模式可以帮助将这些不同的实现封装在不同的状态类中,提高代码的可维护性。
  3. 避免大量的条件语句:当对象的行为涉及多个条件语句时,使用状态模式可以避免代码中出现大量的条件分支,使代码更清晰、更易理解。
  4. 对象的状态需要动态切换:如果对象的状态需要在运行时动态切换,并且需要根据不同状态执行不同的操作,状态模式是一种有效的方式来管理这些状态的切换。
  5. 对象的状态机复杂性较高:当对象的状态机非常复杂且难以直接管理时,状态模式可以将状态机的不同状态和状态转换逻辑分布到不同的状态类中,使整体设计更加清晰。
  6. 需要添加新的状态而不影响现有代码:状态模式使得添加新的状态相对容易,因为可以创建新的具体状态类而不需要修改现有代码。

总之,状态模式适用于那些需要管理对象状态、状态之间存在复杂转换逻辑、以及需要更好地组织和分离状态相关行为的情况。它有助于减少代码的耦合度,提高代码的可维护性和可扩展性。

3. Python应用例子

以下是一个使用 Python 实现状态模式的简单示例,演示了一个电梯的状态管理:

# State接口
class State:
    def handle(self):
        pass

# 具体状态1:电梯停止状态
class StoppedState(State):
    def handle(self):
        print("电梯停止,等待指令")

# 具体状态2:电梯运行状态
class RunningState(State):
    def handle(self):
        print("电梯正在运行")

# 上下文:电梯
class Elevator:
    def __init__(self):
        self.current_state = StoppedState()  # 初始状态为停止状态

    def change_state(self, new_state):
        self.current_state = new_state

    def operate(self):
        self.current_state.handle()

# 客户端代码
if __name__ == "__main__":
    elevator = Elevator()

    elevator.operate()  # 输出 "电梯停止,等待指令"

    elevator.change_state(RunningState())
    elevator.operate()  # 输出 "电梯正在运行"

在这个示例中:

  • State 是状态的抽象基类,定义了状态对象应该实现的方法。
  • StoppedState 和 RunningState 是具体状态类,分别表示电梯的停止状态和运行状态,并实现了 handle() 方法来执行特定状态下的行为。
  • Elevator 是上下文,它包含一个对当前状态对象的引用,并在 operate() 方法中执行当前状态的行为。通过 change_state() 方法,客户端可以切换电梯的状态。

在客户端代码中,我们首先创建一个电梯对象并让它执行操作。然后,我们通过 change_state() 方法将电梯的状态切换为运行状态,并再次执行操作,电梯的行为将根据不同状态而变化。这演示了状态模式的工作原理,状态对象的切换导致了不同的行为。

4. 实现要素

状态模式的实现要素包括以下几个关键元素:

  1. Context(上下文):上下文是拥有状态的对象,它维护一个对当前状态对象的引用,并且在运行时可以切换不同的状态。上下文对象根据当前状态对象的不同来决定执行哪些行为。
  2. State(状态):状态是一个接口或抽象类,它定义了状态对象应该实现的方法。这些方法通常包括了具体行为的定义,每个具体状态都必须实现这些方法。通常,状态接口中包括了多个具体状态共享的行为方法。
  3. ConcreteState(具体状态):具体状态是状态的具体实现类,它实现了状态接口中定义的方法,并根据不同的状态执行相应的行为。每个具体状态类表示了对象的一个特定状态,具体状态类之间是相互独立的。
  4. Client(客户端):客户端是使用状态模式的部分,它创建上下文对象并设置其初始状态,然后通过上下文对象执行操作。客户端通常不需要了解状态对象的具体实现。

5. UML图

以下是状态模式的简化 UML 类图:

+----------------+              +-----------------+
|   Context      |------------->|    State        |
+----------------+              +-----------------+
| - state: State |              |                 |
| + request()    |              | + handle()      |
+----------------+              +-----------------+
                                    /\
                                    |
                                    |
                          +---------+---------+
                          |                   |
                  +-------------+       +-------------+
                  | ConcreteState1 |       | ConcreteState2 |
                  +-------------+       +-------------+
                  | + handle()   |       | + handle()   |
                  +-------------+       +-------------+
  • Context 类包含了对当前状态的引用,并且具有一个 request() 方法用于执行操作。
  • State 接口定义了状态对象应该实现的方法,通常包括 handle() 方法,该方法执行特定状态下的行为。
  • ConcreteState1 和 ConcreteState2 是具体状态类,它们实现了 State 接口中定义的方法,并分别表示不同的状态。

在运行时,Context 对象可以切换不同的具体状态,以改变其行为。这种状态转换是通过修改 Context 对象的 state 引用来实现的。

6. Java/golang/javascrip/C++ 等语言实现方式

6.1 Java实现

上述例子用Java语言实现示例如下:

// State接口
interface State {
    void handle();
}

// 具体状态1:电梯停止状态
class StoppedState implements State {
    @Override
    public void handle() {
        System.out.println("电梯停止,等待指令");
    }
}

// 具体状态2:电梯运行状态
class RunningState implements State {
    @Override
    public void handle() {
        System.out.println("电梯正在运行");
    }
}

// 上下文:电梯
class Elevator {
    private State currentState;

    public Elevator() {
        currentState = new StoppedState(); // 初始状态为停止状态
    }

    public void changeState(State newState) {
        currentState = newState;
    }

    public void operate() {
        currentState.handle();
    }
}

// 客户端代码
public class ElevatorDemo {
    public static void main(String[] args) {
        Elevator elevator = new Elevator();

        elevator.operate(); // 输出 "电梯停止,等待指令"

        elevator.changeState(new RunningState());
        elevator.operate(); // 输出 "电梯正在运行"
    }
}

在这个示例中:

  • State 是状态的接口,定义了状态对象应该实现的方法。
  • StoppedState 和 RunningState 是具体状态类,分别表示电梯的停止状态和运行状态,并实现了 handle() 方法来执行特定状态下的行为。
  • Elevator 是上下文,它包含一个对当前状态对象的引用,并在 operate() 方法中执行当前状态的行为。通过 changeState() 方法,客户端可以切换电梯的状态。

在客户端代码中,我们首先创建一个电梯对象并让它执行操作。然后,我们通过 changeState() 方法将电梯的状态切换为运行状态,并再次执行操作,电梯的行为将根据不同状态而变化。这演示了状态模式的工作原理,状态对象的切换导致了不同的行为。

6.2 Golang实现

上述例子用golang实现示例如下:

package main

import "fmt"

// State接口
type State interface {
        Handle()
}

// 具体状态1:电梯停止状态
type StoppedState struct{}

func (s *StoppedState) Handle() {
        fmt.Println("电梯停止,等待指令")
}

// 具体状态2:电梯运行状态
type RunningState struct{}

func (s *RunningState) Handle() {
        fmt.Println("电梯正在运行")
}

// 上下文:电梯
type Elevator struct {
        currentState State
}

func NewElevator() *Elevator {
        return &Elevator{currentState: &StoppedState{}} // 初始状态为停止状态
}

func (e *Elevator) ChangeState(newState State) {
        e.currentState = newState
}

func (e *Elevator) Operate() {
        e.currentState.Handle()
}

func main() {
        elevator := NewElevator()

        elevator.Operate() // 输出 "电梯停止,等待指令"

        elevator.ChangeState(&RunningState{})
        elevator.Operate() // 输出 "电梯正在运行"
}

在这个示例中:

  • State 是状态的接口,定义了状态对象应该实现的方法。
  • StoppedState 和 RunningState 是具体状态类,分别表示电梯的停止状态和运行状态,并实现了 Handle() 方法来执行特定状态下的行为。
  • Elevator 是上下文,它包含一个对当前状态对象的引用,并在 Operate() 方法中执行当前状态的行为。通过 ChangeState() 方法,客户端可以切换电梯的状态。

在客户端代码中,我们首先创建一个电梯对象并让它执行操作。然后,我们通过 ChangeState() 方法将电梯的状态切换为运行状态,并再次执行操作,电梯的行为将根据不同状态而变化。这演示了状态模式的工作原理,状态对象的切换导致了不同的行为。

6.3 Javascript实现

上述例子用javascript实现示例如下:

// State接口
class State {
    handle() {}
}

// 具体状态1:电梯停止状态
class StoppedState extends State {
    handle() {
        console.log("电梯停止,等待指令");
    }
}

// 具体状态2:电梯运行状态
class RunningState extends State {
    handle() {
        console.log("电梯正在运行");
    }
}

// 上下文:电梯
class Elevator {
    constructor() {
        this.currentState = new StoppedState(); // 初始状态为停止状态
    }

    changeState(newState) {
        this.currentState = newState;
    }

    operate() {
        this.currentState.handle();
    }
}

// 客户端代码
const elevator = new Elevator();

elevator.operate(); // 输出 "电梯停止,等待指令"

elevator.changeState(new RunningState());
elevator.operate(); // 输出 "电梯正在运行"

在这个示例中:

  • State 是状态的基类,定义了状态对象应该实现的方法。
  • StoppedState 和 RunningState 是具体状态类,分别表示电梯的停止状态和运行状态,并实现了 handle() 方法来执行特定状态下的行为。
  • Elevator 是上下文,它包含一个对当前状态对象的引用,并在 operate() 方法中执行当前状态的行为。通过 changeState() 方法,客户端可以切换电梯的状态。

在客户端代码中,我们首先创建一个电梯对象并让它执行操作。然后,我们通过 changeState() 方法将电梯的状态切换为运行状态,并再次执行操作,电梯的行为将根据不同状态而变化。这演示了状态模式的工作原理,状态对象的切换导致了不同的行为。

6.4 C++实现

上述例子用C++实现如下:

#include <iostream>

// 命令接口
class Command {
public:
    virtual void execute() = 0;
};

// 具体命令:打开电视
class TurnOnTVCommand : public Command {
private:
    Television* tv;

public:
    TurnOnTVCommand(Television* tv) : tv(tv) {}

    void execute() override {
        tv->turnOn();
    }
};

// 具体命令:关闭电视
class TurnOffTVCommand : public Command {
private:
    Television* tv;

public:
    TurnOffTVCommand(Television* tv) : tv(tv) {}

    void execute() override {
        tv->turnOff();
    }
};

// 接收者:电视
class Television {
public:
    void turnOn() {
        std::cout << "电视已打开" << std::endl;
    }

    void turnOff() {
        std::cout << "电视已关闭" << std::endl;
    }
};

// 调用者:遥控器
class RemoteControl {
private:
    Command* command;

public:
    RemoteControl() : command(nullptr) {}

    void setCommand(Command* cmd) {
        command = cmd;
    }

    void pressButton() {
        if (command) {
            command->execute();
        }
    }
};

int main() {
    Television tv;
    TurnOnTVCommand turnOnCommand(&tv);
    TurnOffTVCommand turnOffCommand(&tv);

    RemoteControl remote;

    // 打开电视
    remote.setCommand(&turnOnCommand);
    remote.pressButton();

    // 关闭电视
    remote.setCommand(&turnOffCommand);
    remote.pressButton();

    return 0;
}

在这个示例中:

  • State 是状态的抽象基类,定义了状态对象应该实现的方法。
  • StoppedState 和 RunningState 是具体状态类,分别表示电梯的停止状态和运行状态,并实现了 handle() 方法来执行特定状态下的行为。
  • Elevator 是上下文,它包含一个对当前状态对象的引用,并在 operate() 方法中执行当前状态的行为。通过 changeState() 方法,客户端可以切换电梯的状态。

在客户端代码中,我们首先创建一个电梯对象并让它执行操作。然后,我们通过 changeState() 方法将电梯的状态切换为运行状态,并再次执行操作,电梯的行为将根据不同状态而变化。这演示了状态模式的工作原理,状态对象的切换导致了不同的行为。

7. 练习题

假设你正在开发一个电子游戏中的角色系统,每个角色可以处于不同的状态,例如正常状态、受伤状态和死亡状态。角色的状态会影响它的行为和外观。使用状态模式来实现这个角色系统。

要求:

  1. 创建一个 Character 类,它包含一个对当前状态的引用以及一个 performAction() 方法用于执行角色的操作。
  2. 创建一个 State 接口,其中包含一个 handle() 方法,表示不同状态下的处理逻辑。
  3. 实现具体状态类 NormalState、InjuredState 和 DeadState,每个类都实现了 State 接口,并在 handle() 方法中定义了不同状态下的行为。例如,NormalState 下的角色可以攻击,而 DeadState 下的角色不能。
  4. 在客户端代码中,创建一个角色对象并根据不同情况将其状态切换为不同的状态。然后调用 performAction() 方法来查看不同状态下的行为。
  5. 验证角色在不同状态下是否执行了正确的行为。

你可以在评论区里或者私信我回复您的答案,这样我或者大家都能帮你解答,期待着你的回复~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guohuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值