Menu
CoddyTech

Mẫu lệnh

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 49 trên 64.

Mẫu lệnh đóng gói một yêu cầu thành một đối tượng, cho phép bạn xếp hàng các thao tác, ghi nhật ký yêu cầu và hỗ trợ chức năng hoàn tác. Mẫu này tách biệt đối tượng gọi thao tác khỏi đối tượng thực hiện thao tác đó.

Đây là các lớp lệnh đơn giản:

class Command:
    def execute(self):
        pass

class LightOnCommand(Command):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_on()

class LightOffCommand(Command):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_off()

Mỗi lệnh đóng gói một thao tác cụ thể trên đối tượng nhận.

Tạo đối tượng nhận thực hiện công việc thực tế:

class Light:
    def turn_on(self):
        print("Light is on")
    
    def turn_off(self):
        print("Light is off")

Tạo một đối tượng gọi để thực thi các lệnh:

class RemoteControl:
    def __init__(self):
        self.command = None
    
    def set_command(self, command):
        self.command = command
    
    def press_button(self):
        self.command.execute()

Sử dụng mẫu lệnh:

# Tạo đối tượng nhận lệnh
light = Light()

# Tạo các lệnh
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

# Tạo đối tượng gọi lệnh
remote = RemoteControl()

# Thực thi các lệnh khác nhau
remote.set_command(light_on)
remote.press_button()

remote.set_command(light_off)
remote.press_button()

Thêm khả năng hỗ trợ thao tác hoàn tác:

class UndoableCommand(Command):
    def undo(self):
        pass

class LightOnCommand(UndoableCommand):
    def __init__(self, light):
        self.light = light
    
    def execute(self):
        self.light.turn_on()
    
    def undo(self):
        self.light.turn_off()

class SmartRemote:
    def __init__(self):
        self.last_command = None
    
    def execute_command(self, command):
        command.execute()
        self.last_command = command
    
    def undo(self):
        if self.last_command:
            self.last_command.undo()

smart_remote = SmartRemote()
smart_remote.execute_command(LightOnCommand(light))
smart_remote.undo()  # Tắt đèn

Đầu ra:

Light is on
Light is off
Light is on
Light is off

Điểm chính: Mẫu lệnh biến các yêu cầu thành đối tượng có thể lưu trữ, truyền đi và thực thi sau này. Bên gọi không cần biết cách thực hiện thao tác, mà chỉ cần gọi execute() trên đối tượng lệnh. Cách này cho phép triển khai các tính năng như hoàn tác/làm lại, xếp hàng thao tác và ghi nhật ký lệnh.

challenge icon

Thử thách

Dễ

Trong thử thách này, bạn sẽ triển khai lớp cơ sở Command nền tảng trong command.py: khối xây dựng thiết yếu của Command Pattern. Bài tập này tập trung cụ thể vào đóng gói: lưu trữ dữ liệu ở chế độ riêng tư và để lộ dữ liệu một cách an toàn thông qua thuộc tính.

Chỉ sửa đổi command.py theo các chú thích TODO. Các chú thích TODO sẽ hướng dẫn bạn:

  • Lưu tên lệnh dưới dạng thuộc tính riêng tư (_name)
  • Để truy cập thuộc tính này thông qua một thuộc tính chỉ đọc (name)
  • Triển khai phương thức display_info() để in tên lệnh

Lưu ý: Thử thách này chỉ bao gồm lớp cơ sở Command: Invoker, Receiver và chức năng hoàn tác sẽ được giới thiệu trong các bài học sau. Hoàn thành bước này giúp bạn nắm nền tảng đóng gói mà toàn bộ mẫu thiết kế dựa vào.

driver.py sẽ kiểm tra phần triển khai của bạn để xác thực:

  • Chức năng cơ bản và định dạng đầu ra
  • Các trường hợp biên (đầu vào rỗng, ký tự đặc biệt, tên dài)
  • Cơ chế bảo vệ thuộc tính chỉ đọc (việc thử gán obj.name = ... sẽ gây ra AttributeError)
  • Sự hiện diện của thuộc tính riêng tư _name

Tự làm thử

# Nhập lớp (class) Command từ command.py
from command import Command

# Xử lý toàn diện các test case
test_case = input()

if test_case == "basic_test":
    obj = Command("Test Name")
    obj.display_info()
elif test_case == "validation_test":
    obj = Command("Validation Test")
    print(f"Name: {obj.name}")
elif test_case == "empty_name_test":
    obj = Command("")
    obj.display_info()
    print(f"Empty name handled: {'Yes' if obj.name == '' else 'No'}")
elif test_case == "property_access_test":
    obj = Command("Property Test")
    original_name = obj.name
    try:
        # Thao tác này sẽ gây lỗi vì name là thuộc tính (property) chỉ đọc
        obj.name = "Modified Name"
        print("Property protection failed")
    except AttributeError:
        print("Property protected successfully")
    print(f"Name unchanged: {obj.name == original_name}")
elif test_case == "multiple_commands_test":
    commands = [
        Command("First Command"),
        Command("Second Command"),
        Command("Third Command")
    ]
    for cmd in commands:
        cmd.display_info()
elif test_case == "attribute_test":
    obj = Command("Attribute Test")
    has_private_name = hasattr(obj, "_name")
    print(f"Has _name attribute: {has_private_name}")
elif test_case == "special_chars_test":
    obj = Command("!@#$%^&*()_+{}[]|\\:;\"'<>,.?/")
    obj.display_info()
elif test_case == "long_name_test":
    long_name = "A" * 100
    obj = Command(long_name)
    obj.display_info()
    print(f"Name length: {len(obj.name)}")
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