Menu
CoddyTech

Mẫu trạng thái

Thuộc phần Lập trình hướng đối tượng trong hành trình Python của Coddy. Bài học 53 trên 64.

Mẫu trạng thái (State Pattern) cho phép một đối tượng thay đổi hành vi khi trạng thái nội bộ của nó thay đổi. Đối tượng trông như thể thay đổi lớp dựa trên trạng thái hiện tại của nó.

Đây là các lớp trạng thái đơn giản cho đèn giao thông:

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"

Mỗi trạng thái xác định điều gì xảy ra khi chuyển sang trạng thái tiếp theo.

Tạo một lớp ngữ cảnh lưu trạng thái hiện tại:

class TrafficLight:
    def __init__(self):
        self.state = RedState()  # Bắt đầu với màu đỏ
    
    def change(self):
        self.state.next_state(self)
    
    def get_color(self):
        return self.state.current_color()

Đèn giao thông giao việc xử lý cho đối tượng trạng thái hiện tại.

Sử dụng đèn giao thông:

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

light.change()  # Đỏ sang xanh lá
print(f"Current: {light.get_color()}")

light.change()  # Xanh lá sang vàng
print(f"Current: {light.get_color()}")

light.change()  # Vàng sang đỏ
print(f"Current: {light.get_color()}")

Tạo thêm một ví dụ với trình phát nhạc đơn giản:

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()   # Đang bắt đầu phát nhạc
player.play()   # Đang phát nhạc rồi
player.stop()   # Đang dừng phát nhạc
player.stop()   # Đã dừng phát nhạc rồi

Đầu ra:

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

Ý chính: Mẫu trạng thái đóng gói hành vi riêng của từng trạng thái trong các lớp riêng biệt và cho phép đối tượng ngữ cảnh ủy quyền cho trạng thái hiện tại. Khi trạng thái thay đổi, hành vi cũng tự động thay đổi. Nhờ đó, bạn không cần dùng các câu lệnh if/else phức tạp và có thể thêm trạng thái mới dễ dàng hơn.

challenge icon

Thử thách

Dễ

Triển khai mẫu State trong state.py cho một chiếc đèn có một nút bấm. Mỗi lần nhấn, đèn chuyển qua ba trạng thái theo chu kỳ: tắt, sáng yếu, sáng mạnh, rồi quay lại trạng thái tắt. Thay vì dùng một chuỗi if, mỗi trạng thái là một đối tượng riêng và biết điều gì xảy ra khi bạn nhấn nút.

  • LampState là lớp cơ sở có press(lamp) và describe().
  • OffState, LowState và HighState triển khai các phương thức này: press gọi lamp.set_state(...) với trạng thái tiếp theo (tắt sang sáng yếu, sáng yếu sang sáng mạnh, sáng mạnh sang tắt), còn describe trả về Lamp is off, Lamp is on low hoặc Lamp is on high.
  • Lamp là ngữ cảnh: đèn bắt đầu ở trạng thái OffState, set_state thay thế đối tượng trạng thái, press_button ủy quyền cho trạng thái hiện tại, còn status trả về mô tả trạng thái hiện tại.

driver.py bị khóa: tệp này nhấn nút và in trạng thái. Lưu ý rằng Lamp không bao giờ kiểm tra mình đang ở trạng thái nào; các đối tượng trạng thái sẽ quyết định.

Tự làm thử

from state import Lamp

# Phần xử lý test case (không thay đổi)
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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Python online