• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

图解设计模式(16):Mediator模式(只有一个仲裁者)

设计模式 winrains 1年前 (2019-09-23) 52次浏览

组员向仲裁者报告,仲裁者向组员下达指示,组员之间不相互询问和相互指示,这种模式被称为Mediator模式。
一方面,当发生麻烦事情的时候,通知仲裁者。当发生涉及全体组员的事情时,也通知仲裁者。当仲裁者下达指示时,组员会立即执行。团队组员之间不再互相沟通并私自做出决定,而是发生任何事情都向仲裁者报告。
另一方面,仲裁者站在整个团队的角度上对组员上报的事情做出决定。
在Mediator模式中,“仲裁者”被称为Mediator,各组员被称为Colleague。

1 Mediator模式中的角色

  • Mediator(仲裁者)

负责定义与Colleague角色进行通信和做出决定的接口。在示例中,对应Mediator接口。

  • ConcreteMediator(具体的仲裁者)

负责实现Mediator角色的接口,负责实际做出决定。在示例中,对应LoginFrame类。

  • Colleague(同事)

负责定义与Mediator角色进行通信的接口。在示例中,对应Colleague接口。

  • ConcreteColleague(具体的同事)

负责实现Colleague角色的接口。在示例中,对应ColleagueButton、ColleagueTextField、ColleagueCheckbox类。

2 Mediator模式的类图

3 示例程序

是一个GUI应用程序,它展示了一个登录对话框,用户在其中输出正确的用户名和密码后可以登录。
对话框的使用方法如下:
1)可以选择作为游客访问或是作为用户登录;
2)作为用户登录时,需要输入正确的用户名和密码;
3)点击OK按钮可以登录,点击Cancel按钮可以取消登录。
在示例中,并不会真正登录,而是在按下按钮后就退出程序。
需要注意程序的以下行为:

  • 如果选择作为游客访问,那么禁用用户名输入框和密码输入框,使用户无法输入;
  • 如果选择作为用户登录,那么启用用户名输入框和密码输入框,使用户可以输入;
  • 如果在用户名输入框中一个字符都没有输入,那么禁用密码输入框,使用户无法输入密码;
  • 如果用户名输入框中输入了至少一个字符,那么启用密码输入框,使用户可以输入密码;
  • 只有当用户名输入框和密码输入框中都至少输入一个字符后,OK按钮才处于启用状态;
  • Cancel按钮一直启用状态。

3.1 类和接口一览表

名字 说明
Mediator 定义“仲裁者”的接口的接口
Colleague 定义“组员”的接口的接口
ColleagueButton 表示按钮的类。它实现了Colleague接口
ColleagueTextField 表示文本输入框的类。它实现了Colleague接口
ColleagueCheckbox 表示单选按钮的类。它实现了Colleague接口
LoginFrame 表示登录对话框的类。它实现了Mediator接口
Main 测试程序行为的类

3.2 类图

3.3 示例代码

Mediator接口

表示仲裁者的接口。具体的仲裁者会实现这个接口。
createColleagues方法用于生成Mediator要管理的组员。在示例中,该方法会生成对话框中的按钮和文本输入框等控件。
colleagueChanged方法会被各个Colleague组员调用。它的作用是让组员可以向仲裁者进行报告。在本例中,当单选按钮和文本输入框的状态发生变化时,该方法会被调用。

public interface Mediator {
    public abstract void createColleagues();
    public abstract void colleagueChanged();
}

Colleague接口

表示向仲裁者进行报告的组员的接口。具体的组员会实现这个接口。
setMediator方法的作用是告知组员谁是仲裁者。
setCollegueEnabled方法的作用是告知组员仲裁者所下达的指示。参数enabled如果为true,表示自己需要变为“启用状态“;如果为false,则表示自己需要变为”禁用状态“。这个方法表明,究竟是变为”启用状态“还是”禁用状态“,并非由组员自己决定,而是由仲裁者来决定。
需要说明的是,关于Mediator接口和Colleague接口中究竟需要定义哪些方法,是需要需求的不同而不同。

public interface Colleague {
    public abstract void setMediator(Mediator mediator);
    public abstract void setColleagueEnabled(boolean enabled);
}

ColleagueButton类

实现了Colleague接口,与LoginFrameMediator接口)共同工作。

import java.awt.Button;
public class ColleagueButton extends Button implements Colleague {
    private Mediator mediator;
    public ColleagueButton(String caption) {
        super(caption);
    }
    public void setMediator(Mediator mediator) {            // 保存Mediator
        this.mediator = mediator;
    }
    public void setColleagueEnabled(boolean enabled) {      // Mediator下达启用/禁用的指示
        setEnabled(enabled);
    }
}

ColleagueTextField类

通过textValueChanged方法捕捉到文本内容发生变化这一事件,并通知仲裁者。
textValueChanged方法是在TextListener接口中定义的方法。当文本内容发生变化时,AWT框架会调用该方法。

import java.awt.TextField;
import java.awt.Color;
import java.awt.event.TextListener;
import java.awt.event.TextEvent;
public class ColleagueTextField extends TextField implements TextListener, Colleague {
    private Mediator mediator;
    public ColleagueTextField(String text, int columns) {   // 构造函数
        super(text, columns);
    }
    public void setMediator(Mediator mediator) {            // 保存Mediator
        this.mediator = mediator;
    }
    public void setColleagueEnabled(boolean enabled) {      // Mediator下达启用/禁用的指示
        setEnabled(enabled);
        setBackground(enabled ? Color.white : Color.lightGray);
    }
    public void textValueChanged(TextEvent e) {             // 当文字发生变化时通知Mediator
        mediator.colleagueChanged();
    }
}

ColleagueCheckbox类

通过itemStateChanged方法来捕获单选按钮的状态变化。

import java.awt.Checkbox;
import java.awt.CheckboxGroup;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
public class ColleagueCheckbox extends Checkbox implements ItemListener, Colleague {
    private Mediator mediator;
    public ColleagueCheckbox(String caption, CheckboxGroup group, boolean state) {  // 构造函数
        super(caption, group, state);
    }
    public void setMediator(Mediator mediator) {            // 保存Mediator
        this.mediator = mediator;
    }
    public void setColleagueEnabled(boolean enabled) {      // Mediator下达启用/禁用指示
        setEnabled(enabled);
    }
    public void itemStateChanged(ItemEvent e) {             // 当状态发生变化时通知Mediator
        mediator.colleagueChanged();
    }
}

LoginFrame类

实现了Mediator接口。
createColleague方法会生成登录对话框所需的Colleague,并将它们保存在LoginFrame类的字段中。
colleagueChanged方法负责设置控件的启用/禁用的复杂逻辑处理。

import java.awt.Frame;
import java.awt.Label;
import java.awt.Color;
import java.awt.CheckboxGroup;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class LoginFrame extends Frame implements ActionListener, Mediator {
    private ColleagueCheckbox checkGuest;
    private ColleagueCheckbox checkLogin;
    private ColleagueTextField textUser;
    private ColleagueTextField textPass;
    private ColleagueButton buttonOk;
    private ColleagueButton buttonCancel;
    // 构造函数。
    // 生成并配置各个Colleague后,显示对话框。
    public LoginFrame(String title) {
        super(title);
        setBackground(Color.lightGray);
        // 使用布局管理器生成4×2窗格
        setLayout(new GridLayout(4, 2));
        // 生成各个Colleague
        createColleagues();
        // 配置
        add(checkGuest);
        add(checkLogin);
        add(new Label("Username:"));
        add(textUser);
        add(new Label("Password:"));
        add(textPass);
        add(buttonOk);
        add(buttonCancel);
        // 设置初始的启用起用/禁用状态
        colleagueChanged();
        // 显示
        pack();
        show();
    }
    // 生成各个Colleague。
    public void createColleagues() {
        // 生成
        CheckboxGroup g = new CheckboxGroup();
        checkGuest = new ColleagueCheckbox("Guest", g, true);
        checkLogin = new ColleagueCheckbox("Login", g, false);
        textUser = new ColleagueTextField("", 10);
        textPass = new ColleagueTextField("", 10);
        textPass.setEchoChar('*');
        buttonOk = new ColleagueButton("OK");
        buttonCancel = new ColleagueButton("Cancel");
        // 设置Mediator
        checkGuest.setMediator(this);
        checkLogin.setMediator(this);
        textUser.setMediator(this);
        textPass.setMediator(this);
        buttonOk.setMediator(this);
        buttonCancel.setMediator(this);
        // 设置Listener
        checkGuest.addItemListener(checkGuest);
        checkLogin.addItemListener(checkLogin);
        textUser.addTextListener(textUser);
        textPass.addTextListener(textPass);
        buttonOk.addActionListener(this);
        buttonCancel.addActionListener(this);
    }
    // 接收来自于Colleage的通知然后判断各Colleage的启用/禁用状态。
    public void colleagueChanged() {
        if (checkGuest.getState()) { // Guest mode
            textUser.setColleagueEnabled(false);
            textPass.setColleagueEnabled(false);
            buttonOk.setColleagueEnabled(true);
        } else { // Login mode
            textUser.setColleagueEnabled(true);
            userpassChanged();
        }
    }
    // 当textUser或是textPass文本输入框中的文字发生变化时
    // 判断各Colleage的启用/禁用状态
    private void userpassChanged() {
        if (textUser.getText().length() > 0) {
            textPass.setColleagueEnabled(true);
            if (textPass.getText().length() > 0) {
                buttonOk.setColleagueEnabled(true);
            } else {
                buttonOk.setColleagueEnabled(false);
            }
        } else {
            textPass.setColleagueEnabled(false);
            buttonOk.setColleagueEnabled(false);
        }
    }
    public void actionPerformed(ActionEvent e) {
        System.out.println(e.toString());
        System.exit(0);
    }
}

Main类

测试程序。

import java.awt.*;
import java.awt.event.*;
public class Main {
    static public void main(String args[]) {
        new LoginFrame("Mediator Sample");
    }
}

运行结果

4 总结

4.1 当发生分散灾难时

示例程序中的LoginFrame类的colleagueChanged方法稍微有些复杂。如果发生需求变更,该方法中很容易发生bug。不过,由于其他地方并没有控制控件启用/禁用状态的逻辑处理,也很容易在此方法中找出bug的原因。
如果这段逻辑分散在Colleague各个子类中,那么无论是调试代码还是修改代码,都会非常困难。

4.2 哪些角色可以复用

ConcreteColleague角色可以复用,而ConcreteMediator角色很难复用。ConcreteColleague角色中并没有依赖任何特定对话框的代码,而ConcreteMediator则依赖于特定的应用程序,导致难以复用。

摘自《图解设计模式》


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)