中介者模式(学习笔记)

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

相关文章
相关标签/搜索