中介者模式(學習筆記)

  1. 意圖

  用一箇中介對象來封裝一系列的對象交互。中介者是各個對象不須要顯示的相互引用,從而使其耦合鬆散,並且能夠獨立地改變它們之間的交互java

  2. 動機

   面向對象設計鼓勵將行爲分佈到各個對象中。這種分佈可能致使對象間有不少鏈接。在最壞的狀況,每個對象都知道其餘對象的存在。ide

  雖然將一個系統分割成許多對象一般能夠加強可複用性,可是對象間相互鏈接的激增又會下降其可複用性。大量的相互鏈接使得一個對象彷佛不太可能在沒有其餘對象的支持下工做——系統表現爲一個不可分割的總體。這樣,對系統的任何較大改動都十分困難,由於行爲被分佈在許多對象中。結果是,你可能不得不定義不少子類以定製系統的行爲。this

  例如,考慮一個圖形用戶界面中對話框的實現。對話框使用一個窗口來展示一系列的窗口組件,如按鈕、菜單和輸入域等,以下圖所示。spa

   

  一般對話框中的組件間存在依賴關係。例如,當一個特定的輸入域爲空時,某個按鈕不能使用;在稱爲列表框的一列選項中一個表目可能會改變輸入域的內容;一旦文本出如今輸入域中,其餘按鈕可能就變得可以使用了,這些按鈕容許用戶作一些操做,好比改變或刪除文本所指的東西。不一樣的對話框會有不一樣的窗口組件間依賴關係。所以,即便對話框顯示相同類型的窗口組件,也沒辦法簡單的直接複用已有的窗口組件類,而必須定製以反映特定對話框的依賴關係。因爲涉及多個類,用逐個生成子類的辦法來定製會很冗長設計

·  經過將集體行爲封裝在一個單獨的中介者對象中來避免這個問題。中介者充當一箇中介以使組中的對象不在相互的顯式引用。這些對象僅知道中介者的存在,從而減小了鏈接的數目code

  例如,FontDialogDirector可做爲一個對話框中的窗口組件間的中介者,充當窗口組件間通訊的中轉中心component

  

 

  下面的交互圖說明了各對象如何協做處理一個列表框中選項的變化:orm

  下面一系列事件使得一個列表框的選擇被傳送給一個輸入域:對象

  1) ListBox通知中介者它被改變了blog

  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()

相關文章
相關標籤/搜索