中介

用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

中介模式(Mediator)又称调停者模式,它的目的是把多方会谈变成双方会谈,从而实现多方的松耦合。

有些童鞋听到中介立刻想到房产中介,立刻气不打一处来。这个中介模式与房产中介还真有点像,所以消消气,先看例子。

考虑一个简单的点餐输入:

这个小系统有4个参与对象:

  • 多选框;
  • “选择全部”按钮;
  • “取消所有”按钮;
  • “反选”按钮。

它的复杂性在于,当多选框变化时,它会影响“选择全部”和“取消所有”按钮的状态(是否可点击),当用户点击某个按钮时,例如“反选”,除了会影响多选框的状态,它又可能影响“选择全部”和“取消所有”按钮的状态。

所以这是一个多方会谈,逻辑写起来很复杂:

  1. ┌─────────────────┐ ┌─────────────────┐
  2. CheckBox List │<───>│SelectAll Button
  3. └─────────────────┘ └─────────────────┘
  4. └─────────────────────┤
  5. ┌─────────────────┐ ┌────────┴────────┐
  6. SelectNone Button│<────│ Inverse Button
  7. └─────────────────┘ └─────────────────┘

如果我们引入一个中介,把多方会谈变成多个双方会谈,虽然多了一个对象,但对象之间的关系就变简单了:

  1. ┌─────────────────┐
  2. ┌─────>│ CheckBox List
  3. └─────────────────┘
  4. ┌─────────────────┐
  5. ┌───>│SelectAll Button
  6. └─────────────────┘
  7. ┌─────────┐
  8. Mediator
  9. └─────────┘
  10. ┌─────────────────┐
  11. └───>│SelectNone Button
  12. └─────────────────┘
  13. ┌─────────────────┐
  14. └─────>│ Inverse Button
  15. └─────────────────┘

下面我们用中介模式来实现各个UI组件的交互。首先把UI组件给画出来:

  1. public class Main {
  2. public static void main(String[] args) {
  3. new OrderFrame("Hanburger", "Nugget", "Chip", "Coffee");
  4. }
  5. }
  6. class OrderFrame extends JFrame {
  7. public OrderFrame(String... names) {
  8. setTitle("Order");
  9. setSize(460, 200);
  10. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  11. Container c = getContentPane();
  12. c.setLayout(new FlowLayout(FlowLayout.LEADING, 20, 20));
  13. c.add(new JLabel("Use Mediator Pattern"));
  14. List<JCheckBox> checkboxList = addCheckBox(names);
  15. JButton selectAll = addButton("Select All");
  16. JButton selectNone = addButton("Select None");
  17. selectNone.setEnabled(false);
  18. JButton selectInverse = addButton("Inverse Select");
  19. setVisible(true);
  20. }
  21. private List<JCheckBox> addCheckBox(String... names) {
  22. JPanel panel = new JPanel();
  23. panel.add(new JLabel("Menu:"));
  24. List<JCheckBox> list = new ArrayList<>();
  25. for (String name : names) {
  26. JCheckBox checkbox = new JCheckBox(name);
  27. list.add(checkbox);
  28. panel.add(checkbox);
  29. }
  30. getContentPane().add(panel);
  31. return list;
  32. }
  33. private JButton addButton(String label) {
  34. JButton button = new JButton(label);
  35. getContentPane().add(button);
  36. return button;
  37. }
  38. }

然后,我们设计一个Mediator类,它引用4个UI组件,并负责跟它们交互:

  1. public class Mediator {
  2. // 引用UI组件:
  3. private List<JCheckBox> checkBoxList;
  4. private JButton selectAll;
  5. private JButton selectNone;
  6. private JButton selectInverse;
  7. public Mediator(List<JCheckBox> checkBoxList, JButton selectAll, JButton selectNone, JButton selectInverse) {
  8. this.checkBoxList = checkBoxList;
  9. this.selectAll = selectAll;
  10. this.selectNone = selectNone;
  11. this.selectInverse = selectInverse;
  12. // 绑定事件:
  13. this.checkBoxList.forEach(checkBox -> {
  14. checkBox.addChangeListener(this::onCheckBoxChanged);
  15. });
  16. this.selectAll.addActionListener(this::onSelectAllClicked);
  17. this.selectNone.addActionListener(this::onSelectNoneClicked);
  18. this.selectInverse.addActionListener(this::onSelectInverseClicked);
  19. }
  20. // 当checkbox有变化时:
  21. public void onCheckBoxChanged(ChangeEvent event) {
  22. boolean allChecked = true;
  23. boolean allUnchecked = true;
  24. for (var checkBox : checkBoxList) {
  25. if (checkBox.isSelected()) {
  26. allUnchecked = false;
  27. } else {
  28. allChecked = false;
  29. }
  30. }
  31. selectAll.setEnabled(!allChecked);
  32. selectNone.setEnabled(!allUnchecked);
  33. }
  34. // 当点击select all:
  35. public void onSelectAllClicked(ActionEvent event) {
  36. checkBoxList.forEach(checkBox -> checkBox.setSelected(true));
  37. selectAll.setEnabled(false);
  38. selectNone.setEnabled(true);
  39. }
  40. // 当点击select none:
  41. public void onSelectNoneClicked(ActionEvent event) {
  42. checkBoxList.forEach(checkBox -> checkBox.setSelected(false));
  43. selectAll.setEnabled(true);
  44. selectNone.setEnabled(false);
  45. }
  46. // 当点击select inverse:
  47. public void onSelectInverseClicked(ActionEvent event) {
  48. checkBoxList.forEach(checkBox -> checkBox.setSelected(!checkBox.isSelected()));
  49. onCheckBoxChanged(null);
  50. }
  51. }

运行一下看看效果:

mediator

使用Mediator模式后,我们得到了以下好处:

  • 各个UI组件互不引用,这样就减少了组件之间的耦合关系;
  • Mediator用于当一个组件发生状态变化时,根据当前所有组件的状态决定更新某些组件;
  • 如果新增一个UI组件,我们只需要修改Mediator更新状态的逻辑,现有的其他UI组件代码不变。

Mediator模式经常用在有众多交互组件的UI上。为了简化UI程序,MVC模式以及MVVM模式都可以看作是Mediator模式的扩展。

练习

中介 - 图2下载练习:使用Mediator模式 (推荐使用IDE练习插件快速下载)

小结

中介模式是通过引入一个中介对象,把多边关系变成多个双边关系,从而简化系统组件的交互耦合度。

读后有收获可以支付宝请作者喝咖啡,读后有疑问请加微信群讨论:

中介 - 图3中介 - 图4