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.
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 raAttributeError) - 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)}")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
1Nền tảng về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTham số selfPhương thứcThuộc tínhPhương thức khởi tạo (__init__)Ôn tập: máy tính đơn giản4Kế thừa
Kế thừa cơ bảnHàm super()Ghi đè phương thứcKế thừa đa lớpThứ tự phân giải phương thứcÔn tập: hệ thống phân cấp nhân viên2Decorator
Giới thiệu về decoratorDecorator thuộc tínhDecorator phương thức tĩnhDecorator phương thức lớp5Tính đa hình
Xem lại cách ghi đè phương thứcKiểu gõ vịtLớp và phương thức trừu tượngThiết kế giao diệnÔn tập: máy tính hình học8Các khái niệm OOP nâng cao
Kết hợp và kế thừaMixinPhương thức tĩnh và phương thức lớpDecorator lớpTrình quản lý ngữ cảnh11Mẫu thiết kế phần 2
Mẫu lệnhMẫu bộ chuyển đổiMẫu trang tríMẫu phương thức khuôn mẫuMẫu trạng tháiMẫu tổng hợpTự luyện tập: Trình biên dịch Python online