Menu
CoddyTech

状态模式

本课属于 Coddy Python 学习旅程的“面向对象编程”单元。第 53 课,共 64 课。

状态模式允许对象在内部状态发生变化时改变其行为。对象会根据当前状态呈现出不同的类。

下面是交通灯的简单状态类:

class RedState:
    def next_state(self, light):
        print("Red -> Green")
        light.state = GreenState()
    
    def current_color(self):
        return "Red"

class GreenState:
    def next_state(self, light):
        print("Green -> Yellow")
        light.state = YellowState()
    
    def current_color(self):
        return "Green"

class YellowState:
    def next_state(self, light):
        print("Yellow -> Red")
        light.state = RedState()
    
    def current_color(self):
        return "Yellow"

每个状态都会定义切换到下一个状态时发生的情况。

创建一个保存当前状态的上下文类:

class TrafficLight:
    def __init__(self):
        self.state = RedState()  # 从红灯开始
    
    def change(self):
        self.state.next_state(self)
    
    def get_color(self):
        return self.state.current_color()

交通信号灯将行为委托给其当前状态对象。

使用交通灯:

light = TrafficLight()
print(f"Current: {light.get_color()}")

light.change()  # 红灯变绿灯
print(f"Current: {light.get_color()}")

light.change()  # 绿灯变黄灯
print(f"Current: {light.get_color()}")

light.change()  # 黄灯变红灯
print(f"Current: {light.get_color()}")

再用一个简单的播放器创建示例:

class PlayingState:
    def play(self, player):
        print("Already playing")
    
    def stop(self, player):
        print("Stopping music")
        player.state = StoppedState()

class StoppedState:
    def play(self, player):
        print("Starting music")
        player.state = PlayingState()
    
    def stop(self, player):
        print("Already stopped")

class MusicPlayer:
    def __init__(self):
        self.state = StoppedState()
    
    def play(self):
        self.state.play(self)
    
    def stop(self):
        self.state.stop(self)

player = MusicPlayer()
player.play()   # 开始播放音乐
player.play()   # 正在播放
player.stop()   # 停止播放音乐
player.stop()   # 已经停止

输出:

Current: Red
Red -> Green
Current: Green
Green -> Yellow
Current: Yellow
Yellow -> Red
Current: Red
Starting music
Already playing
Stopping music
Already stopped

要点:状态模式将特定状态的行为封装在单独的类中,并让上下文对象将任务委托给当前状态。状态改变时,行为也会自动改变。这样可以避免复杂的 if/else 语句,也让添加新状态变得更容易。

challenge icon

挑战

简单

在 state.py 中为一盏只有一个按钮的灯实现状态模式。每按一次按钮,灯就会在三个状态之间循环切换:关、低亮、高亮,然后回到关。不要用针对字符串的 if 链,而是让每个状态都成为一个对象,由它决定按下按钮时会发生什么。

  • LampState 是基类,包含 press(lamp) 和 describe()。
  • OffState、LowState 和 HighState 实现这两个方法:press 调用 lamp.set_state(...) 并传入下一个状态(从关到低亮,从低亮到高亮,从高亮到关),而 describe 返回 Lamp is off、Lamp is on low 或 Lamp is on high。
  • Lamp 是上下文对象:它从 OffState 开始,set_state 会替换状态对象,press_button 会将调用委托给当前状态,而 status 会返回当前状态的描述。

driver.py 已锁定:它会按下按钮并输出状态。注意,Lamp 从不检查自己处于哪个状态,状态对象会自行决定。

亲手试试

from state import Lamp

# 测试用例处理程序(不要修改)
test_case = input()
lamp = Lamp()

if test_case == "initial_test":
    print(lamp.status())
elif test_case == "one_press_test":
    lamp.press_button()
    print(lamp.status())
elif test_case == "cycle_test":
    for _ in range(4):
        print(lamp.status())
        lamp.press_button()
elif test_case == "many_presses_test":
    for _ in range(7):
        lamp.press_button()
    print(lamp.status())
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Python 在线编译器