中介者在 Java 中的实现
中介者是一种行为设计模式, 让程序组件通过特殊的中介者对象进行间接沟通, 达到减少组件之间依赖关系的目的。
中介者能使得程序更易于修改和扩展, 而且能更方便地对独立的组件进行复用, 因为它们不再依赖于很多其他的类。
在 Java 中使用模式
复杂度:⭐⭐
流行度:⭐⭐
使用示例: 中介者模式在 Java 代码中最常用于帮助程序 GUI 组件之间的通信。 在 MVC 模式中, 控制器是中介者的同义词。
下面是核心 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 元素组织起来, 使其在中介者的帮助下无需相互依赖就能合作。
components: Colleague classes
components/Component.java
package refactoring_guru.mediator.example.components; import refactoring_guru.mediator.example.mediator.Mediator; /** * Common component interface. */ public interface Component { void setMediator(Mediator mediator); String getName(); }
components/AddButton.java
package refactoring_guru.mediator.example.components; import refactoring_guru.mediator.example.mediator.Mediator; import refactoring_guru.mediator.example.mediator.Note; import javax.swing.*; import java.awt.event.ActionEvent; /** * Concrete components don't talk with each other. They have only one * communication channel–sending requests to the mediator. */ 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"; } }
components/DeleteButton.java
package refactoring_guru.mediator.example.components; import refactoring_guru.mediator.example.mediator.Mediator; import javax.swing.*; import java.awt.event.ActionEvent; /** * Concrete components don't talk with each other. They have only one * communication channel–sending requests to the mediator. */ public class DeleteButton extends JButton implements Component { private Mediator mediator; public DeleteButton() { super("Del"); } @Override public void setMediator(Mediator mediator) { this.mediator = mediator; } @Override protected void fireActionPerformed(ActionEvent actionEvent) { mediator.deleteNote(); } @Override public String getName() { return "DelButton"; } }
components/Filter.java
package refactoring_guru.mediator.example.components; import refactoring_guru.mediator.example.mediator.Mediator; import refactoring_guru.mediator.example.mediator.Note; import javax.swing.*; import java.awt.event.KeyEvent; import java.util.ArrayList; /** * Concrete components don't talk with each other. They have only one * communication channel–sending requests to the mediator. */ 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 listModel) { this.listModel = listModel; } 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"; } }
components/List.java
package refactoring_guru.mediator.example.components; import refactoring_guru.mediator.example.mediator.Mediator; import refactoring_guru.mediator.example.mediator.Note; import javax.swing.*; /** * Concrete components don't talk with each other. They have only one * communication channel–sending requests to the mediator. */ @SuppressWarnings("unchecked") public class List extends JList implements Component { private Mediator mediator; private final DefaultListModel LIST_MODEL; public List(DefaultListModel listModel) { super(listModel); this.LIST_MODEL = listModel; setModel(listModel); 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) {} } 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 ex) { ex.printStackTrace(); } if (list.isSelectionEmpty()) { mediator.hideElements(true); } else { mediator.hideElements(false); } } } } }
components/SaveButton.java
package refactoring_guru.mediator.example.components; import refactoring_guru.mediator.example.mediator.Mediator; import javax.swing.*; import java.awt.event.ActionEvent; /** * Concrete components don't talk with each other. They have only one * communication channel–sending requests to the mediator. */ public class SaveButton extends JButton implements Component { private Mediator mediator; public SaveButton() { super("Save"); } @Override public void setMediator(Mediator mediator) { this.mediator = mediator; } @Override protected void fireActionPerformed(ActionEvent actionEvent) { mediator.saveChanges(); } @Override public String getName() { return "SaveButton"; } }
components/TextBox.java
package refactoring_guru.mediator.example.components; import refactoring_guru.mediator.example.mediator.Mediator; import javax.swing.*; import java.awt.event.KeyEvent; /** * Concrete components don't talk with each other. They have only one * communication channel–sending requests to the mediator. */ 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"; } }
components/Title.java
package refactoring_guru.mediator.example.components; import refactoring_guru.mediator.example.mediator.Mediator; import javax.swing.*; import java.awt.event.KeyEvent; /** * Concrete components don't talk with each other. They have only one * communication channel–sending requests to the mediator. */ public class Title extends JTextField 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 "Title"; } }
mediator
mediator/Mediator.java: 定义通用的中介者接口
package refactoring_guru.mediator.example.mediator; import refactoring_guru.mediator.example.components.Component; import javax.swing.*; /** * Common mediator interface. */ 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(); }
mediator/Editor.java: 具体中介者
package refactoring_guru.mediator.example.mediator; import refactoring_guru.mediator.example.components.*; import refactoring_guru.mediator.example.components.Component; import refactoring_guru.mediator.example.components.List; import javax.swing.*; import javax.swing.border.LineBorder; import java.awt.*; /** * Concrete mediator. All chaotic communications between concrete components * have been extracted to the mediator. Now components only talk with the * mediator, which knows who has to handle a request. */ 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..."); /** * Here the registration of components by the mediator. */ @Override public void registerComponent(Component component) { component.setMediator(this); switch (component.getName()) { case "AddButton": add = (AddButton)component; break; case "DelButton": 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; } } /** * Various methods to handle requests from particular components. */ @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 ignored) {} } @Override public void markNote() { try { Note note = list.getCurrentElement(); String name = note.getName(); if (!name.endsWith("*")) { note.setName(note.getName() + "*"); } list.repaint(); } catch (NullPointerException ignored) {} } @Override public void clear() { title.setText(""); textBox.setText(""); } @Override public void sendToFilter(ListModel listModel) { filter.setList(listModel); } @SuppressWarnings("unchecked") @Override public void setElementsList(ListModel list) { this.list.setModel(list); this.list.repaint(); } @Override public void hideElements(boolean flag) { titleLabel.setVisible(!flag); textLabel.setVisible(!flag); title.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); } }
mediator/Note.java: 笔记类
package refactoring_guru.mediator.example.mediator; /** * Note class. */ public class Note { private String name; private String text; public Note() { name = "New note"; } public void setName(String name) { this.name = name; } public void setText(String text) { this.text = text; } public String getName() { return name; } public String getText() { return text; } @Override public String toString() { return name; } }
Demo.java: 初始化代码
package refactoring_guru.mediator.example; import refactoring_guru.mediator.example.components.*; import refactoring_guru.mediator.example.mediator.Editor; import refactoring_guru.mediator.example.mediator.Mediator; import javax.swing.*; /** * Demo class. Everything comes together here. */ 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 DeleteButton()); mediator.registerComponent(new SaveButton()); mediator.registerComponent(new List(new DefaultListModel())); mediator.registerComponent(new Filter()); mediator.createGUI(); } }
OutputDemo.png: 执行结果