设计模式:Mediator模式

Mediator模式——只有一个仲裁者

想象一个乱糟糟的开发小组的工作状态,组员之间虽然在一起协同工作,但是意见难以统一,总是相互指挥,导致工作进度滞后。这时候,就需要一个仲裁者。所有的组员将自己的情况汇报给仲裁者,仲裁者会从团队的整体考虑,然后下达指令。仲裁者负责统一大家的意见。这样,团队的交流过程就变成了组员向仲裁者报告,仲裁者向组员下达指令的形式,而组员之间不再询问和沟通,这就是Mediator模式。

如果你对Mediator模式的重要性认识不充分,考虑下面一个示例程序。

这是一个用户的登录对话框:

这里写图片描述

  • 可以选择作为游客访问或是作为用户登录

  • 作为用户登录时,需要输入正确的用户名和密码

  • 点击OK按钮可以登录,点击Cancel按钮可以取消登录

这看似是一个简单的程序,但是仔细思考一下程序的行为,你会发现一下的问题。

  • 如果选择作为游客访问,那么禁用用户名输入框和密码输入框,使用户无法输入

  • 如果选择作为用户登录,那么启用用户名输入框和密码输入框,使用户可以输入

  • 如果在用户名输入框中一个字符都没有输入,那么禁用密码输入框,使用户无法输入密码

  • 如果在用户名输入框中输入了至少一个字符,那么启用密码输入框,使用户可以输入密码(如果选择游客访问,那么密码框仍然是禁用状态)

  • 只有当用户名输入框和密码输入框都至少输入了一个字符后,OK按钮才处于启用状态,可以被按下。反之,则禁用OK按钮,使其不可被按下(如果选择游客访问,那么OK按钮总是处于启用状态)

  • Cancel按钮总是处于启用状态,任何时候都可以按下该按钮

设想一下,如果把上面的功能全部实现,需要编写怎样的代码。如果将上面的逻辑处理分散在各个类中,那么编码的工作量会变得非常大这是因为所有的对象都是相互关联,相互制约的。这样要调整多个对象之间的关系时,就需要用到Mediator模式了。即不让各个对象之间相互通信,而是增加一个仲裁者角色,让他们各自与仲裁者通信。然后,将控制显示的逻辑处理交给仲裁者负责。

下面给出了一个示例程序。

  • 类和接口的一览表
名字 说明
Mediator 定义“仲裁者”的接口(API)
Colleague 定义“组员”的接口(API)
ColleagueButton 表示按钮的类。它实现了Colleague接口
ColleagueTextField 表示文本输入框的类。它实现了Colleague接口
ColleagueCheckbox 表示单选按钮的类。它实现了Colleague接口
LoginFrame 表示登录对话框的类。它实现了Mediator接口
Main 测试程序行为的类
  • Mediator接口
public interface Mediator {
    public abstract void createColleagues();

    public abstract void colleagueChanged();
}
  • Colleague接口
public interface Colleague {
    public abstract void setMediator(Mediator mediator);

    public abstract void setColleagueEnabled(boolean enabled);
}
  • ColleagueButton类
import java.awt.*;

public class ColleagueButton extends Button
        implements Colleague {
    private Mediator mediator;

    public ColleagueButton(String caption) {
        super(caption);
    }

    @Override
    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }

    @Override
    public void setColleagueEnabled(boolean enabled) {
        setEnabled(enabled);
    }
}
  • ColleagueTextField类
import java.awt.*;
import java.awt.event.TextEvent;
import java.awt.event.TextListener;

public class ColleagueTextField extends TextField
        implements TextListener, Colleague {
    private Mediator mediator;

    public ColleagueTextField(String text, int columns) {
        super(text, columns);
    }

    @Override
    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }

    @Override
    public void setColleagueEnabled(boolean enabled) {
        setEnabled(enabled);
        setBackground(enabled ? Color.WHITE : Color.lightGray);
    }

    @Override
    public void textValueChanged(TextEvent e) {
        mediator.colleagueChanged();
    }
}
  • ColleagueCheckbox类
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

public class ColleagueCheckBox extends Checkbox
        implements ItemListener, Colleague {
    private Mediator mediator;

    public ColleagueCheckBox(
            String caption, CheckboxGroup group, boolean state) {
        super(caption, group, state);
    }

    @Override
    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }

    @Override
    public void setColleagueEnabled(boolean enabled) {
        setEnabled(enabled);
    }

    @Override
    public void itemStateChanged(ItemEvent e) {
        mediator.colleagueChanged();
    }
}
  • LoginFrame类
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

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;

    public LoginFrame(String title) {
        super(title);
        setBackground(Color.lightGray);
        setLayout(new GridLayout(4, 2));
        createColleagues();
        add(checkGuest);
        add(checkLogin);
        add(new Label("Username:"));
        add(textUser);
        add(new Label("Password:"));
        add(textPass);
        add(buttonOk);
        add(buttonCancel);
        // 设置初始的启用/禁用状态
        colleagueChanged();
        setPreferredSize(new Dimension(300,200));
        pack();
        setVisible(true);
    }

    @Override
    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");
        checkGuest.setMediator(this);
        checkLogin.setMediator(this);
        textUser.setMediator(this);
        textPass.setMediator(this);
        buttonOk.setMediator(this);
        buttonCancel.setMediator(this);
        checkGuest.addItemListener(checkGuest);
        checkLogin.addItemListener(checkLogin);
        textUser.addTextListener(textUser);
        textPass.addTextListener(textPass);
        buttonOk.addActionListener(this);
        buttonCancel.addActionListener(this);
    }

    @Override
    public void colleagueChanged() {
        if (checkGuest.getState()) {
            textUser.setColleagueEnabled(false);
            textPass.setColleagueEnabled(false);
            buttonOk.setColleagueEnabled(true);
        } else {
            textUser.setColleagueEnabled(true);
            userpassChanged();
        }
    }

    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);
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println(e.toString());
        System.exit(0);
    }
}
  • Main类
public class Main {
    public static void main(String[] args) {
        new LoginFrame("Mediator Sample");
    }
}

执行结果如下:

  • 图1 如果选择作为游客访问,那么禁用用户名输入框和密码输入框

这里写图片描述

  • 图2 如果选择作为用户登录,那么用户名输入框处于启用状态,密码输入框处于禁用状态

这里写图片描述

  • 图3 如果在用户名输入框中输入了字符,那么密码输入框处于启用状态,OK按钮处于禁用状态

这里写图片描述

  • 图4 如果又输入了密码,那么OK按钮处于可按下状态

这里写图片描述

  • 图5 即使输入了密码,但只要删除了用户名,OK按钮和密码输入框就会变为禁用状态

这里写图片描述


Mediator模式中的角色

  • Mediator(仲裁者)

Mediator角色负责定义与Colleague角色经营通信和做出决定的接口(API)。在示例程序中,由Mediator接口扮演此角色。

  • ConcreteMediator(具体的仲裁者)

ConcreteMediator角色负责实现Mediator角色的接口(API),负责实际作出决定。在示例程序中,由LoginFrame类扮演此角色。

  • Colleague(同事)

Colleague角色负责定义与Mediator角色通信的接口(API)。在示例程序中,由Colleague接口扮演此角色。

  • ConcreteColleague(具体的同事)

ConcreteColleague角色负责实现Colleague角色的接口(API)。在示例程序中,由ColleagueButton类,ColleagueTextField类和ColleagueCheckbox类扮演此角色。


Mediator模式的思路

不容易发生分散灾难。如果需求发生变更,由于其他地方并没有控制控件的启用/禁用的逻辑处理,因此只要调试该方法就能很容易地找出Bug的原因。

通常情况下,面向对象编程可以帮助我们分散处理,避免处理过于集中,也就是说可以“分而治之”。但是如果只是将应当分散的处理分散在各个类中,但是没有将应当集中的处理集中起来,那么这些分散的类最终只会导致灾难。


想了解更多关于设计模式:设计模式专栏

猜你喜欢

转载自blog.csdn.net/qq_39384184/article/details/80631275
今日推荐