1. 意圖
用一個中介物件來封裝一系列的物件互動,中介者是各個物件不需要顯示的相互參考,從而使其耦合松散,而且可以獨立地改變它們之間的互動
2. 動機
面向物件設計鼓勵將行為分布到各個物件中,這種分布可能導致物件間有很多連接,在最壞的情況,每一個物件都知道其他物件的存在,
雖然將一個系統分割成許多物件通常可以增強可復用性,但是物件間相互連接的激增又會降低其可復用性,大量的相互連接使得一個物件似乎不太可能在沒有其他物件的支持下作業——系統表現為一個不可分割的整體,這樣,對系統的任何較大改動都十分困難,因為行為被分布在許多物件中,結果是,你可能不得不定義很多子類以定制系統的行為,
例如,考慮一個圖形用戶界面中對話框的實作,對話框使用一個視窗來展現一系列的視窗組件,如按鈕、選單和輸入域等,如下圖所示,
通常對話框中的組件間存在依賴關系,例如,當一個特定的輸入域為空時,某個按鈕不能使用;在稱為串列框的一列選項中一個表目可能會改變輸入域的內容;一旦文本出現在輸入域中,其他按鈕可能就變得能夠使用了,這些按鈕允許用戶做一些操作,比如改變或洗掉文本所指的東西,不同的對話框會有不同的視窗組件間依賴關系,因此,即使對話框顯示相同型別的視窗組件,也沒辦法簡單的直接復用已有的視窗組件類,而必須定制以反映特定對話框的依賴關系,由于涉及多個類,用逐個生成子類的辦法來定制會很冗長
· 通過將集體行為封裝在一個單獨的中介者物件中來避免這個問題,中介者充當一個中介以使組中的物件不在相互的顯式參考,這些物件僅知道中介者的存在,從而減少了連接的數目
例如,FontDialogDirector可作為一個對話框中的視窗組件間的中介者,充當視窗組件間通信的中轉中心

下面的互動圖說明了各物件如何協作處理一個串列框中選項的變化:
下面一系列事件使得一個串列框的選擇被傳送給一個輸入域:
1) ListBox通知中介者它被改變了
2) 中介者從ListBox中得到選中的選項
3) 中介者將該選項傳遞給EntryField
4) 現在EnrryField已有文本,導控者使得用于發起一個動作(如”黑體“,”斜體“)的某個按鈕可用
3. 適用性
- 一組物件以定義良好但復雜的方式進行通信,產生的相互依賴關系結構混亂且難以理解
- 一個物件參考其他很多物件并且直接與這些物件通信,導致難以復用該物件
- 如果為了能在不同情景下復用一些基本行為,導致你需要被迫創建大量組件子類時,可使用中介者模式(所有組件間關系都被包含在中介者中,因此你無需修改組件就能方便地新建中介者類以定義新的組件合作方式)
4. 結構
5. 效果
1) 減少子類的生成 Mediator將原本分布于多個物件間的行為集中在一起,改變這些行為只需生成Mediator的子類即可,這樣各個colleague可以復用
2)將各個Colleague解耦 Mediator有利于各Colleague間的松耦合,可以獨立的改變和復用各Colleague類和Mediator類(開閉原則)
3)簡化了物件協議 用Mediator和各Colleague間的一對多互動來代替多對多互動,一對多更便于理解、維護和擴展
4)使控制集中化 中介者模式將互動的復雜性變為中介者的復雜性,中介者封裝了協議,使得它變得比任一個Colleague都復雜,這使得中介者本身變成一個難于維護的龐然大物
6. 代碼實作
本例展示了如何將許多 GUI 元素組織起來, 使其在中介者的幫助下無需相互依賴就能合作
components/Component.java
package mediator.components; import mediator.mediator.Mediator; /** * @author GaoMing * @date 2021/7/25 - 20:23 * Common component interface. */ public interface Component { void setMediator(Mediator mediator); String getName(); }
components/AddButton.java
package mediator.components; import mediator.mediator.Mediator; import mediator.mediator.Note; import javax.swing.*; import java.awt.event.ActionEvent; /** * @author GaoMing * @date 2021/7/25 - 20:20 * 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 mediator.components; import mediator.mediator.Mediator; import javax.swing.*; import java.awt.event.ActionEvent; /** * @author GaoMing * @date 2021/7/25 - 20:21 * 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 mediator.components; import mediator.mediator.Mediator; import mediator.mediator.Note; import javax.swing.*; import java.awt.event.KeyEvent; import java.util.ArrayList; /** * @author GaoMing * @date 2021/7/25 - 20:21 * 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 mediator.components; import mediator.mediator.Mediator; import mediator.mediator.Note; import javax.swing.*; /** * @author GaoMing * @date 2021/7/25 - 20:21 * 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 mediator.components; import mediator.mediator.Mediator; import javax.swing.*; import java.awt.event.ActionEvent; /** * @author GaoMing * @date 2021/7/25 - 20:21 * 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 mediator.components; import mediator.mediator.Mediator; import javax.swing.*; import java.awt.event.KeyEvent; /** * @author GaoMing * @date 2021/7/25 - 20:21 * 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 mediator.components; import mediator.mediator.Mediator; import javax.swing.*; import java.awt.event.KeyEvent; /** * @author GaoMing * @date 2021/7/25 - 20:21 * 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.java: 定義通用的中介者介面
package mediator.mediator; import javax.swing.*; import mediator.components.Component; /** * @author GaoMing * @date 2021/7/25 - 20:31 * 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 mediator.mediator; import mediator.components.*; import mediator.components.Component; import mediator.components.List; import javax.swing.*; import javax.swing.border.LineBorder; import java.awt.*; /** * @author GaoMing * @date 2021/7/25 - 20:22 * 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); } }View Code
mediator/Note.java: 筆記類
package mediator.mediator; /** * @author GaoMing * @date 2021/7/25 - 20:22 */ 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 mediator; import mediator.components.*; import mediator.mediator.Mediator; import mediator.mediator.Editor; import javax.swing.*; /** * @author GaoMing * @date 2021/7/25 - 20:20 */ 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(); } }
運行結果
7. 與其他模式的關系
-
責任鏈模式、命令模式、中介者模式和觀察者模式用于處理請求發送者和接收者之間的不同連接方式:
- 責任鏈按照順序將請求動態傳遞給一系列的潛在接收者,直至其中一名接收者對請求進行處理
- 命令在發送者和請求者之間建立單向連接
- 中介者清除了發送者和請求者之間的直接連接,強制它們通過一個中介物件進行間接溝通
- 觀察者允許接收者動態地訂閱或取消接收請求 -
外觀模式和中介者的職責類似:它們都嘗試在大量緊密耦合的類中組織起合作
- 外觀為子系統中的所有物件定義了一個簡單介面,但是它不提供任何新功能,子系統本身不會意識到外觀的存在,子系統中的物件可以直接進行交流
- 中介者將系統中組件的溝通行為中心化,各組件只知道中介者物件,無法直接相互交流 -
有一種流行的中介者模式實作方式依賴于觀察者,中介者物件擔當發布者的角色,其他組件則作為訂閱者,可以訂閱中介者的事件或取消訂閱,當中介者以這種方式實作時,它可能看上去與觀察者非常相似
8. 已知應用
使用示例:中介者模式在Java 代碼中最常用于幫助程式GUI組件之間的通信,在MVC模式中,控制器是中介者的同義詞
下面是核心Java程式庫中該模式的一些示例:
java.util.Timer (所有 schedule-XXX()方法)
java.util.concurrent.Executor#execute()
java.util.concurrent.ExecutorService ( invoke-XXX()和 submit-()方法)
java.util.concurrent.ScheduledExecutorService (所有 schedule-XXX()方法)
java.lang.reflect.Method#invoke()
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/308447.html
標籤:設計模式
上一篇:職責鏈模式(學習筆記)
下一篇:命令模式(學習筆記)
