中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。
下面是核心 Java 程序库中该模式的一些示例:
- java.util.Timer (所有
scheduleXXX()
方法) - java.util.concurrent.Executor#execute()
- java.util.concurrent.ExecutorService (
invokeXXX()
和submit()
方法) - java.util.concurrent.ScheduledExecutorService (所有
scheduleXXX()
方法) - java.lang.reflect.Method#invoke()
中介者模式结构
样例
笔记程序
本例展示了如何将许多 GUI 元素组织起来, 使其在中介者的帮助下无需相互依赖就能合作。
Component 接口
package behavioral.mediator.components;
import behavioral.mediator.mediators.Mediator;
public interface Component {
void setMediator(Mediator mediator);
String getName();
}
AddButton、DeleteButton、SaveButton
package behavioral.mediator.components;
import behavioral.mediator.mediators.Mediator;
import behavioral.mediator.mediators.Note;
import javax.swing.*;
import java.awt.event.ActionEvent;
public class AddButton extends JButton implements Component {
private Mediator mediator;
public AddButton() {
super("Add");
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
protected void fireActionPerformed(ActionEvent actionEvent) {
mediator.addNewNote(new Note());
}
@Override
public String getName() {
return "AddButton";
}
}
Filter
package behavioral.mediator.components;
import behavioral.mediator.mediators.Mediator;
import behavioral.mediator.mediators.Note;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
public class Filter extends JTextField implements Component {
private Mediator mediator;
private ListModel listModel;
public Filter() {
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
protected void processComponentKeyEvent(KeyEvent keyEvent) {
String start = getText();
searchElements(start);
}
public void setList(ListModel list) {
this.listModel = list;
}
private void searchElements(String s) {
if (listModel == null) return;
if (s.equals("")) {
mediator.setElementsList(listModel);
return;
}
ArrayList<Note> notes = new ArrayList<>();
for (int i = 0; i < listModel.getSize(); i++) {
notes.add((Note) listModel.getElementAt(i));
}
DefaultListModel<Note> listModel = new DefaultListModel<>();
for (Note note : notes) {
if (note.getName().contains(s)) {
listModel.addElement(note);
}
}
mediator.setElementsList(listModel);
}
@Override
public String getName() {
return "Filter";
}
}
List
package behavioral.mediator.components;
import behavioral.mediator.mediators.Mediator;
import behavioral.mediator.mediators.Note;
import javax.swing.*;
public class List extends JList implements Component {
private Mediator mediator;
private final DefaultListModel LIST_MODEL;
public List(DefaultListModel list_model) {
super(list_model);
this.LIST_MODEL = list_model;
setModel(list_model);
this.setLayoutOrientation(JList.VERTICAL);
Thread thread = new Thread(new Hide(this));
thread.start();
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
public void addElement(Note note) {
LIST_MODEL.addElement(note);
int index = LIST_MODEL.size() - 1;
setSelectedIndex(index);
ensureIndexIsVisible(index);
mediator.sendToFilter(LIST_MODEL);
}
public void deleteElement() {
int index = this.getSelectedIndex();
try {
LIST_MODEL.remove(index);
mediator.sendToFilter(LIST_MODEL);
;
} catch (ArrayIndexOutOfBoundsException ignored) {
ignored.printStackTrace();
}
}
public Note getCurrentElement() {
return (Note) getSelectedValue();
}
@Override
public String getName() {
return "List";
}
private class Hide implements Runnable {
private List list;
Hide(List list) {
this.list = list;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.isSelectionEmpty()) {
mediator.hideElements(true);
} else {
mediator.hideElements(false);
}
}
}
}
}
Title、TextBox
package behavioral.mediator.components;
import behavioral.mediator.mediators.Mediator;
import javax.swing.*;
import java.awt.event.KeyEvent;
public class TextBox extends JTextArea implements Component {
private Mediator mediator;
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
protected void processComponentKeyEvent(KeyEvent keyEvent) {
mediator.markNote();
}
@Override
public String getName() {
return "TextBox";
}
}
Mediator 通用的中介者接口
package behavioral.mediator.mediators;
import behavioral.mediator.components.Component;
import javax.swing.*;
public interface Mediator {
void addNewNote(Note note);
void deleteNote();
void getInfoFromList(Note note);
void saveChanges();
void markNote();
void clear();
void sendToFilter(ListModel listModel);
void setElementsList(ListModel list);
void registerComponent(Component component);
void hideElements(boolean flag);
void createGUI();
}
Editor 具体的中介者
package behavioral.mediator.mediators;
import behavioral.mediator.components.*;
import behavioral.mediator.components.Component;
import behavioral.mediator.components.List;
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
public class Editor implements Mediator {
private Title title;
private TextBox textBox;
private AddButton add;
private DeleteButton del;
private SaveButton save;
private List list;
private Filter filter;
private JLabel titleLabel = new JLabel("Title:");
private JLabel textLabel = new JLabel("Text:");
private JLabel label = new JLabel("Add or select existing note to proceed...");
@Override
public void addNewNote(Note note) {
title.setText("");
textBox.setText("");
list.addElement(note);
}
@Override
public void deleteNote() {
list.deleteElement();
}
@Override
public void getInfoFromList(Note note) {
title.setText(note.getName().replace('*', ' '));
textBox.setText(note.getText());
}
@Override
public void saveChanges() {
try {
Note note = (Note) list.getSelectedValue();
note.setName(title.getText());
note.setText(textBox.getText());
list.repaint();
} catch (NullPointerException e) {
e.printStackTrace();
}
}
@Override
public void markNote() {
try {
Note note = list.getCurrentElement();
String name = note.getName();
if (!name.endsWith("*")) {
note.setName(note.getName() + "*");
}
list.repaint();
} catch (NullPointerException e) {
e.printStackTrace();
}
}
@Override
public void clear() {
title.setText("");
textBox.setText("");
}
@Override
public void sendToFilter(ListModel listModel) {
filter.setList(listModel);
}
@Override
public void setElementsList(ListModel list) {
this.list.setModel(list);
this.list.repaint();
}
@Override
public void registerComponent(Component component) {
component.setMediator(this);
switch (component.getName()) {
case "AddButton":
add = (AddButton) component;
break;
case "DeleteButton":
del = (DeleteButton) component;
break;
case "Filter":
filter = (Filter) component;
break;
case "List":
list = (List) component;
this.list.addListSelectionListener(listSelectionEvent -> {
Note note = (Note) list.getSelectedValue();
if (note != null) {
getInfoFromList(note);
} else {
clear();
}
});
break;
case "SaveButton":
save = (SaveButton) component;
break;
case "TextBox":
textBox = (TextBox) component;
break;
case "Title":
title = (Title) component;
break;
default:
break;
}
}
@Override
public void hideElements(boolean flag) {
titleLabel.setVisible(!flag);
textLabel.setVisible(!flag);
textBox.setVisible(!flag);
save.setVisible(!flag);
label.setVisible(flag);
}
@Override
public void createGUI() {
JFrame notes = new JFrame("Notes");
notes.setSize(960, 600);
notes.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel left = new JPanel();
left.setBorder(new LineBorder(Color.BLACK));
left.setSize(320, 600);
left.setLayout(new BoxLayout(left, BoxLayout.Y_AXIS));
JPanel filterPanel = new JPanel();
filterPanel.add(new JLabel("Filter:"));
filter.setColumns(20);
filterPanel.add(filter);
filterPanel.setPreferredSize(new Dimension(280, 40));
JPanel listPanel = new JPanel();
list.setFixedCellWidth(260);
listPanel.setSize(320, 470);
JScrollPane scrollPane = new JScrollPane(list);
scrollPane.setPreferredSize(new Dimension(275, 410));
listPanel.add(scrollPane);
JPanel buttonPanel = new JPanel();
add.setPreferredSize(new Dimension(85, 25));
buttonPanel.add(add);
del.setPreferredSize(new Dimension(85, 25));
buttonPanel.add(del);
buttonPanel.setLayout(new FlowLayout());
left.add(filterPanel);
left.add(listPanel);
left.add(buttonPanel);
JPanel right = new JPanel();
right.setLayout(null);
right.setSize(640, 600);
right.setLocation(320, 0);
right.setBorder(new LineBorder(Color.BLACK));
titleLabel.setBounds(20, 4, 50, 20);
title.setBounds(60, 5, 555, 20);
textLabel.setBounds(20, 4, 50, 130);
textBox.setBorder(new LineBorder(Color.DARK_GRAY));
textBox.setBounds(20, 80, 595, 410);
save.setBounds(270, 535, 80, 25);
label.setFont(new Font("Verdana", Font.PLAIN, 22));
label.setBounds(100, 240, 500, 100);
right.add(label);
right.add(titleLabel);
right.add(title);
right.add(textLabel);
right.add(textBox);
right.add(save);
notes.setLayout(null);
notes.getContentPane().add(left);
notes.getContentPane().add(right);
notes.setResizable(false);
notes.setLocationRelativeTo(null);
notes.setVisible(true);
}
}
初始化代码
package behavioral.mediator;
import behavioral.mediator.components.*;
import behavioral.mediator.mediators.Editor;
import behavioral.mediator.mediators.Mediator;
import javax.swing.*;
public class Demo {
public static void main(String[] args) {
Mediator mediator = new Editor();
mediator.registerComponent(new Title());
mediator.registerComponent(new TextBox());
mediator.registerComponent(new AddButton());
mediator.registerComponent(new SaveButton());
mediator.registerComponent(new DeleteButton());
mediator.registerComponent(new List(new DefaultListModel()));
mediator.registerComponent(new Filter());
mediator.createGUI();
}
}
适用场景
-
当一些对象和其他对象紧密耦合以致难以对其进行修改时, 可使用中介者模式。
该模式让你将对象间的所有关系抽取成为一个单独的类, 以使对于特定组件的修改工作独立于其他组件。
-
当组件因过于依赖其他组件而无法在不同应用中复用时, 可使用中介者模式。
应用中介者模式后, 每个组件不再知晓其他组件的情况。 尽管这些组件无法直接交流,但它们仍可通过中介者对象进行间接交流。 如果你希望在不同应用中复用一个组件, 则需要为其提供一个新的中介者类。
-
如果为了能在不同情景下复用一些基本行为, 导致你需要被迫创建大量组件子类时, 可使用中介者模式。
由于所有组件间关系都被包含在中介者中, 因此你无需修改组件就能方便地新建中介者类以定义新的组件合作方式。
实现方式
- 找到一组当前紧密耦合, 且提供其独立性能带来更大好处的类 (例如更易于维护或更方便复用)。
- 声明中介者接口并描述中介者和各种组件之间所需的交流接口。 在绝大多数情况下, 一个接收组件通知的方法就足够了。 如果你希望在不同情景下复用组件类, 那么该接口将非常重要。 只要组件使用通用接口与其中介者合作, 你就能将该组件与不同实现中的中介者进行连接。
- 实现具体中介者类。 该类可从自行保存其下所有组件的引用中受益。
- 你可以更进一步, 让中介者负责组件对象的创建和销毁。 此后, 中介者可能会与工厂或外观类似。
- 组件必须保存对于中介者对象的引用。 该连接通常在组件的构造函数中建立, 该函数会将中介者对象作为参数传递。
- 修改组件代码, 使其可调用中介者的通知方法, 而非其他组件的方法。 然后将调用其他组件的代码抽取到中介者类中, 并在中介者接收到该组件通知时执行这些代码。
中介者模式优点
- 单一职责原则。 你可以将多个组件间的交流抽取到同一位置, 使其更易于理解和维护。
- 开闭原则。 你无需修改实际组件就能增加新的中介者。
- 你可以减轻应用中多个组件间的耦合情况。
- 你可以更方便地复用各个组件。
中介者模式缺点
一段时间后, 中介者可能会演化成为上帝对象。