命令模式
本课属于 Coddy Python 学习旅程的“面向对象编程”单元。第 49 课,共 64 课。
命令模式将请求封装为对象,让你能够对操作进行排队、记录请求,并支持撤销功能。它将调用操作的对象与执行操作的对象分离。
以下是一些简单的命令类:
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()每个命令都封装了对接收者对象执行的特定操作。
创建执行实际工作的接收者:
class Light:
def turn_on(self):
print("Light is on")
def turn_off(self):
print("Light is off")创建一个执行命令的调用者:
class RemoteControl:
def __init__(self):
self.command = None
def set_command(self, command):
self.command = command
def press_button(self):
self.command.execute()使用命令模式:
# 创建接收者
light = Light()
# 创建命令
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)
# 创建调用者
remote = RemoteControl()
# 执行不同的命令
remote.set_command(light_on)
remote.press_button()
remote.set_command(light_off)
remote.press_button()添加对撤销操作的支持:
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() # 关灯输出:
Light is on
Light is off
Light is on
Light is off要点:命令模式将请求转换为对象,这些对象可以被存储、传递并在之后执行。调用者不需要知道如何执行操作,只需在命令对象上调用 execute()。这样就能实现撤销/重做、将操作加入队列以及记录命令等功能。
挑战
简单在这个挑战中,你将实现 command.py 中的基础 Command 基类,它是命令模式的核心构建块。本练习重点关注封装:私下存储数据,并通过属性安全地公开数据。
请根据 TODO 注释,只修改 command.py。TODO 注释会引导你完成以下操作:
- 将命令名称存储为私有属性(
_name) - 通过只读属性(
name)公开它 - 实现一个
display_info()方法,用于打印命令名称
注意:本挑战只涉及基础 Command 类:调用者、接收者和撤销功能会在后续课程中介绍。完成这一步后,你就掌握了封装基础,完整模式将建立在此基础之上。
你的实现将由 driver.py 测试,它会检查:
- 基本功能和输出格式
- 边缘情况(空输入、特殊字符、较长名称)
- 只读属性保护(尝试
obj.name = ...应引发AttributeError) - 是否存在私有
_name属性
亲手试试
# 从 command.py 导入 Command 类
from command import Command
# 综合测试用例处理程序
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:
# 这应该会失败,因为 name 是只读属性
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)}")这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Python 在线编译器