简介
中介者是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。该模式会限制对象之间的直接交互,强制让它们通过一个中介者对象进行合作。
问题
假如你有一个创建和修改用户资料的对话框,它由各种控件组成,例如文本框 ( T e x t F i e l d )、 复 选框 ( C h e c k b o x ) 和按钮 ( B u t t o n ) 等 。
某些表单元素之间可能会直接进行互动。比如,选中“我有一只猫”复选框后可能会显示一个隐藏文本框用于输入猫咪的名字。另一个例子是点击提交按钮后需要校验所有输入的内容。
另外,如果直接在表单元素代码中实现业务逻辑,你就很难在程序其他表单中复用这些元素类。比如,由于复选框类与猫咪的文本框相耦合,所以就不能在其他表单中使用该复选框。所以要么使用渲染表单时用到的所有类, 要么一个都不用。
解决
中介者模式建议你停止组件之间的直接调用,让他们相互独立。这些组件必须调用中介者对象,通过中介者对象重定向调用行为,用间接的方式进行合作。最终,组件只依赖于一个中介者类,无需跟多个其他组件相耦合。
在编辑表单的例子中,对话框(Dialog) 类本身可以作为中介者 ,他很可能知道自己所有的子元素,因此不需要在这个类里引入新的依赖关系。
绝大部分重要的修改都在实际表单元素里进行。 我们还是回到提交按钮。之前,当用戶点击按钮后,它必须对所有表单内容进行校验。而 现在它的唯一工作是把点击事件通知给对话框。收到通知后,对话框可以自行校验数值或把任务委派给各个元素。这样一来,按钮不再与多个表单元素相关联, 而只依赖于对话框类。
你还可以为所有类型的对话框抽取通用接口,进一步削弱它的依赖性。接口里声明一个所有表单元素都能使用的通知方法,可用于把元素中发生的事件通知给对话框。这样一来,所有实现了这个接口的对话框都能使用这个提交按钮了。
采用这种方式,中介者模式让你能在单个中介者对象中封装多个对象之间的复杂关系网。我们知道,类所拥有的依赖关系越少,就越易于修改、扩展或 复用。
代码
// 1.Mediator接口
interface DialogMediator {
void notify(Component sender, String event); // 统一事件通知口
}
// 2.Concrete Mediator(登录对话框)
class LoginDialogMediator implements DialogMediator {
private CheckBox rememberMeBox; // ComponentA
private TextBox usernameTextBox; // ComponentB
private TextBox passwordTextBox; // ComponentC
private Button submitButton; // ComponentD
public LoginDialogMediator() {
// 组件在中介内创建并建立双向引用
rememberMeBox = new CheckBox(this);
usernameTextBox = new TextBox(this);
passwordTextBox = new TextBox(this);
submitButton = new Button(this);
}
@Override
public void notify(Component sender, String event) {
if (sender == rememberMeBox && event.equals("check")) {
toggleRememberMeConfig();
} else if ((sender == usernameTextBox || sender == passwordTextBox)
&& event.equals("keypress")) {
validateForm(); // 实时校验表单
} else if (sender == submitButton && event.equals("click")) {
handleSubmit();
}
}
private void toggleRememberMeConfig() {
System.out.println("记住密码配置更改:" + rememberMeBox.isChecked());
}
private void validateForm() {
boolean isValid = !usernameTextBox.getText().isEmpty() &&
passwordTextBox.getText().length() >= 6;
submitButton.setEnabled(isValid); // 联合控制按钮状态
}
private void handleSubmit() {
System.out.println("提交用户:" + usernameTextBox.getText());
}
}
// 3.Components实现
abstract class Component {
protected DialogMediator dialog; // 持中介引用
public Component(DialogMediator dialog) {
this.dialog = dialog;
}
public abstract void triggerEvent(String event);
}
class CheckBox extends Component { // ComponentA
private boolean checked;
public CheckBox(DialogMediator dialog) { super(dialog); }
public void toggle() {
checked = !checked;
dialog.notify(this, "check"); // 通知中介
}
public boolean isChecked() { return checked; }
@Override
public void triggerEvent(String event) { toggle(); }
}
class TextBox extends Component { // ComponentB/C
private String text = "";
public TextBox(DialogMediator dialog) { super(dialog); }
public void input(String text) {
this.text = text;
dialog.notify(this, "keypress"); // 实时反馈输入
}
public String getText() { return text; }
@Override
public void triggerEvent(String event) { input(event); }
}
class Button extends Component { // ComponentD
private boolean enabled = false;
public Button(DialogMediator dialog) { super(dialog); }
public void click() {
if(enabled) dialog.notify(this, "click");
}
public void setEnabled(boolean state) {
enabled = state;
System.out.println("按钮状态:" + (state?"可用":"禁用"));
}
@Override
public void triggerEvent(String event) { click(); }
}
// 4.Client使用
public class AuthDialogClient {
public static void main(String[] args) {
LoginDialogMediator dialog = new LoginDialogMediator();
// 模拟用户交互流程
dialog.getUsernameTextBox().triggerEvent("john"); // 输入用户名
dialog.getPasswordTextBox().triggerEvent("123456"); // 输入密码
dialog.getSubmitButton().triggerEvent("click"); // 成功提交
dialog.getRememberMeBox().triggerEvent("check"); // 切换"记住我"
dialog.getPasswordTextBox().triggerEvent("123"); // 触发按钮禁用
}
}
架构优势
- 解耦组件:各组件仅依赖中介对象,无直接关联
- 集中控制:表单验证逻辑统一在validateForm()处理
- 扩展灵活:新增密码强度校验组件时不影响现有结构
总结
- 组件(Component)是各种包含业务逻辑的类。每个组件都有一 个指向中介者的引用,这个引用被声明成中介者接口类型。组件不知道中介者实际所属的类,因此你可以通过把它连接到不同的中介者来让它能在其他程序里复用。
- 中介者(Mediator)接口声明了与组件交流的方法,但通常只包 括一个通知方法。组件可把任意上下文(包括自己的对象)作为 这个方法的参数, 只有这样接收组件和发送者类之间才不会耦合。
- 具体中介者(Concrete Mediator) 封装了多种组件间的关系。 具体中介者通常会保存所有组件的引用并对其进行管理,甚至有时会对它的生命周期进行管理。
- 组件并不知道其他组件的情况。如果组件内发生了重要事件, 它只能通知中介者。中介者收到通知后能轻易地确定发送者,这就已经足够判断接下来需要触发的组件了。 对于组件来说,中介者看上去完全就是一个黑箱。发送者不知道最终会由谁来处理自己的请求,接收者也不知道最初是谁发出了请求。