中介者模式练习——聊天室与表单协调
按《Python 设计模式——中介者模式》的建议,通过两道练习巩固:① 聊天室(用户只通过中介者发消息);② 表单控件协调(输入框与按钮通过中介者联动)。每步都有完整可运行代码和验证要点。
练习一:聊天室(用户通过中介者发消息)
目的
体会同事对象不直接引用彼此,只和中介者通信;发消息时调“中介者.send(我, 内容)”,由中介者决定把消息转给谁(如广播给除自己外的所有人)。
要求
- 实现 ChatRoom(中介者):能添加用户,提供 send(sender, message);send 被调用时,把 message 广播给除 sender 以外的所有已添加用户(即对每个其他用户调用其 receive(sender, message))。
- 实现 User(同事):有 name,构造时传入 Mediator;提供 say(message),内部调 mediator.send(self, message);提供 receive(sender, message),被中介者调用时打印一条“[自己的名字] 收到 [发送者名字] 的消息: 内容”。
- 客户代码:创建一个 ChatRoom,添加 3 个 User,让其中两个用户各 say 一句,验证另外两个用户都收到对应消息,且发送者自己不会收到自己发的消息。
参考答案
from abc import ABC, abstractmethod
class Mediator(ABC):
@abstractmethod
def send(self, sender, message: str):
pass
class ChatRoom(Mediator):
def __init__(self):
self._users = []
def add_user(self, user: "User"):
self._users.append(user)
def send(self, sender, message: str):
for u in self._users:
if u != sender:
u.receive(sender, message)
class User:
def __init__(self, name: str, mediator: Mediator):
self.name = name
self._mediator = mediator
def say(self, message: str):
self._mediator.send(self, message)
def receive(self, sender, message: str):
print(f" [{self.name}] 收到 {sender.name} 的消息: {message}")
# 使用
room = ChatRoom()
alice = User("Alice", room)
bob = User("Bob", room)
carol = User("Carol", room)
room.add_user(alice)
room.add_user(bob)
room.add_user(carol)
print("Alice 发言:")
alice.say("大家好")
print("Bob 发言:")
bob.say("你好")
验证要点
- Alice 说“大家好”时,Bob 和 Carol 都打印“收到 Alice 的消息: 大家好”,Alice 自己不打印(因为 send 里跳过了 sender)。
- Bob 说“你好”时,Alice 和 Carol 都打印“收到 Bob 的消息: 你好”,Bob 自己不打印。
- 确认:User 之间没有直接引用,只通过
_mediator.send(self, message)发消息;谁收到、怎么转发全由 ChatRoom 决定。
练习二:表单控件协调(输入框与按钮通过中介者联动)
目的
多个控件(如两个输入框、一个按钮)不直接互相引用;输入框内容变化或按钮被点击时,只通知中介者(表单);由中介者决定“何时启用按钮”“提交时收集哪些数据”,体会交互逻辑集中在中介者。
要求
- 实现 FormMediator(中介者):能设置 3 个组件——用户名输入框、密码输入框、提交按钮;提供 notify(sender, event),其中
- event == “changed” 时:若两个输入框都非空则启用按钮,否则禁用按钮;
- event == “clicked” 且 sender 是提交按钮时:打印“提交: 用户名=xxx, 密码=xxx”(从两个输入框取值)。
- 实现 InputBox(同事):有 value(字符串),有 set_value(v)(设置后调 mediator.notify(self, “changed”));构造时传入 FormMediator。
- 实现 Button(同事):有 enabled 状态,有 set_enabled(True/False),有 click()(若 enabled 则调 mediator.notify(self, “clicked”));构造时传入 FormMediator。
- 客户代码:创建表单、两个输入框、一个按钮并 set_components;先只填用户名不填密码,点提交应无输出(或按钮不可点);再填密码后点提交,应打印提交内容。