Detailed explanation of intermediary model

Posted by ambivalent on Mon, 17 Jan 2022 03:10:46 +0100

1. Introduction

In real life, there are often complex interaction relationships between many objects. This interaction relationship is often a "mesh structure", which requires each object to know the object it needs to interact with. For example, everyone must remember the phone numbers of all his or her friends; Moreover, if someone's phone number is modified, he or she must let all other friends modify it together. This is called "pulling one hair and moving the whole body", which is very complex.
If this "network structure" is changed to "star structure", the "coupling" between them will be greatly reduced. At this time, just find an "intermediary". As mentioned earlier, the problem of "everyone must remember the phone numbers of all friends" can be solved by establishing an "address book" that every friend can access on the Internet. There are many such Liezi. For example, if you just want to rent a house, you can find a "housing intermediary"; Or, if you have just found a job in a strange city, you can ask the "talent exchange center" for help.
In the process of software development, there are many such examples. For example, in MVC framework, controller (C) is the mediator of model (M) and view (V); Also, the "intermediary" of the commonly used QQ chat program is the QQ server. All these can be realized by "mediator mode", which will greatly reduce the coupling between objects and improve the flexibility of the system.

2. Definitions

Definition of mediator pattern: define a mediation object to encapsulate the interaction between a series of objects, so that the coupling between the original objects is loose, and the interaction between them can be changed independently. Mediator model is also called mediation model, which is a typical application of Dimitri's law.

3. Advantages

  1. Each class performs its own duties, which conforms to Dimitri's law.
  2. It reduces the coupling between objects and makes objects easy to be reused independently.
  3. The one-to-many association between objects is transformed into one-to-one association, which improves the flexibility of the system and makes the system easy to maintain and expand.

4. Disadvantages

The mediation model turns the direct interdependence of multiple objects into the dependency between mediators and multiple colleague classes. When there are more colleagues, intermediaries will become more bloated, complex and difficult to maintain.

5. Structure

The key to the realization of the mediator model is to find out the "mediator".
The main roles of the mediator model are as follows:

  1. Abstract Mediator role: it is the interface of the Mediator and provides abstract methods for registering and forwarding colleague object information.
  2. Concrete Mediator role: implement the mediator interface, define a List to manage colleague objects and coordinate the interaction between various colleague roles. Therefore, it depends on colleague roles.
  3. Abstract Colleague class role: define the interface of Colleague class, save the mediator object, provide abstract methods for Colleague object interaction, and realize the public functions of all interacting Colleague classes.
  4. Concrete Colleague role: it is the implementer of the abstract simultaneous class. When it is necessary to interact with other colleague objects, the mediator object is responsible for the subsequent interaction.

The structure diagram is as follows:

6. Application scenarios

  1. When there is a complex network structure relationship between objects, resulting in confusion of dependencies and difficult to reuse.
  2. When you want to create an object running between multiple classes and don't want to generate new subclasses.

7. Code example

1. Simple example

/**
 * Abstract mediation class
 */
interface IMediator{
    void register(Colleague colleague);
    void relay(Colleague colleague);
}

/**
 * Specific intermediary class
 */
class ConcreteMediator implements IMediator{
    private List<Colleague> colleagues = new ArrayList<>();
    @Override
    public void register(Colleague colleague) {
        if(!colleagues.contains(colleague)){
            colleagues.add(colleague);
            colleague.setMediator(this);
        }
    }
    @Override
    public void relay(Colleague colleague) {
        for(Colleague ob : colleagues){
            if(!ob.equals(colleague)){
                ob.receive();
            }
        }
    }
}
/**
 * Abstract colleague class
 */
abstract class Colleague{
    @Setter
    protected IMediator mediator;
    public abstract void receive();
    public abstract void send();
}

/**
 * Specific category A
 */
class ConcreteColleagueA extends Colleague{
    @Override
    public void receive() {
        System.out.println("Specific colleagues A Request received!");
    }

    @Override
    public void send() {
        System.out.println("Specific colleagues A Make a request!");
        mediator.relay(this);
    }
}

/**
 * Specific category B
 */
class ConcreteColleagueB extends Colleague{
    @Override
    public void receive() {
        System.out.println("Specific colleagues B Request received!");
    }

    @Override
    public void send() {
        System.out.println("Specific colleagues B Make a request!");
        mediator.relay(this);
    }
}
public class MediaPatternSimpleTest {
    public static void main(String[] args){
        IMediator mediator = new ConcreteMediator();
        Colleague colleagueA = new ConcreteColleagueA();
        Colleague colleagueB = new ConcreteColleagueB();

        mediator.register(colleagueA);
        mediator.register(colleagueB);

        colleagueA.send();

        System.out.println("----------------");

        colleagueB.send();
    }
}

2. Application examples

Example: write a program of "Shaoguan real estate exchange platform" with the intermediary model.
Note: Shaoguan real estate exchange platform is a platform provided by "real estate intermediary company" for information exchange between "seller's customer" and "buyer's customer", which is more suitable to be realized by intermediary mode.
First, define a Medium interface, which is an abstract mediator, including the customer registration method register(Customer member) and the information forwarding method relay(String from, String ad); Then define a Shaoguan real estate intermediary company, which is a specific intermediary class. It contains a List object that stores customer information, and implements the abstract methods in the intermediary company.
Then, define a Customer class, which is an abstract colleague class, including the object of the mediator and the interface between the send(String ad) method sending information and the receive(String from, String ad) method receiving information. Since this program is a form program, this class inherits the JPmme class and implements the action time processing method actionPerformed(ActionEvent e).
Finally, the Seller class and the Buyer class are defined. They are specific colleagues and subclasses of the Customer class. They implement the abstract methods in the parent class and exchange information through intermediaries.
The structure diagram is as follows:

Code example:

/**
 * Abstract intermediary: intermediary company
 */
interface IMedium{
    /**
     * Customer registration
     * @param member
     */
    void register(Customer member);

    /**
     * forward
     * @param from
     * @param ad
     */
    void relay(String from, String ad);
}

/**
 * Specific intermediary: real estate intermediary
 */
class EstateMedium implements IMedium{
    private List<Customer> members = new ArrayList<>();
    @Override
    public void register(Customer member) {
        if(!members.contains(member)){
            members.add(member);
            member.setMedium(this);
        }
    }

    @Override
    public void relay(String from, String ad) {
        for(Customer ob : members){
            String name = ob.getName();
            if(!name.equals(from)){
                ob.receive(from, ad);
            }
        }
    }
}
/**
 * Abstract colleague class: customer
 */
abstract class Customer extends JFrame implements ActionListener{
    @Setter
    protected IMedium medium;
    @Getter
    protected String name;
    JTextField sendText;
    JTextArea receiveArea;
    public Customer(String name){
        super(name);
        this.name = name;
    }
    void ClientWindow(int x, int y){
        Container cp;
        JScrollPane sp;
        JPanel p1, p2;
        cp = this.getContentPane();
        sendText = new JTextField(18);
        receiveArea = new JTextArea(10, 18);
        p1 = new JPanel();
        p1.setBorder(BorderFactory.createTitledBorder("Received content:"));
        p1.add(receiveArea);
        sp = new JScrollPane(p1);
        cp.add(sp, BorderLayout.NORTH);
        p2 = new JPanel();
        p2.add(sendText);
        cp.add(p2, BorderLayout.SOUTH);
        sendText.addActionListener(this);
        this.setLocation(x, y);
        this.setSize(250, 330);
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent e){
        String tempInfo = sendText.getText().trim();
        sendText.setText("");
        this.send(tempInfo);
    }
    public abstract void send(String ad);
    public abstract void receive(String from, String ad);
}

/**
 * Specific category: seller
 */
class Seller extends Customer{
    public Seller(String name) {
        super(name);
        ClientWindow(50, 100);
    }

    @Override
    public void send(String ad) {
        receiveArea.append("I (the seller) said:" + ad + "\n");
        //Scroll the scroll bar to the lowest position
        receiveArea.setCaretPosition(receiveArea.getText().length());
        medium.relay(name, ad);
    }

    @Override
    public void receive(String from, String ad) {
        receiveArea.append(from + "Say:" + ad + "\n");
        receiveArea.setCaretPosition(receiveArea.getText().length());
    }
}

/**
 * Specific category: buyer
 */
class Buyer extends Customer{
    public Buyer(String name) {
        super(name);
        ClientWindow(350, 100);
    }

    @Override
    public void send(String ad) {
        receiveArea.append("I (the buyer) said:" + ad + "\n");
        receiveArea.setCaretPosition(receiveArea.getText().length());
        medium.relay(name, ad);
    }

    @Override
    public void receive(String from, String ad) {
        receiveArea.append(from + "Say:" + ad + "\n");
        receiveArea.setCaretPosition(receiveArea.getText().length());
    }
}
public class MediatorEstateMediumTest{
    public static void main(String[] args){
        IMedium medium = new EstateMedium();
        Customer memberA, memberB;
        memberA = new Seller("Zhang San (seller)");
        memberB = new Buyer("Li Si (buyer)");
        medium.register(memberA);
        medium.register(memberB);
    }
}

Operation results:

3. Extension of application mode

In actual development, the following two methods are usually used to simplify the mediator model and make the development easier.

  1. The mediator interface is not defined, and the specific mediator object is implemented as a single instance.
  2. The colleague object does not hold the mediator, but directly obtains the mediator object and calls it when needed.

The structure diagram is as follows:

Code example:

/**
 * Simple singleton mediator
 */
class SimpleMediator{
    private static SimpleMediator simpleMediator = new SimpleMediator();
    private List<ISimpleColleague> colleagues = new ArrayList<>();
    private SimpleMediator(){}
    public static SimpleMediator getMedium(){
        return simpleMediator;
    }
    public void register(ISimpleColleague colleague){
        if(!colleagues.contains(colleague)){
            colleagues.add(colleague);
        }
    }
    public void relay(ISimpleColleague colleague){
        for(ISimpleColleague coll : colleagues){
            if(!coll.equals(colleague)){
                coll.receive();
            }
        }
    }
}

/**
 * Abstract colleague class
 */
interface ISimpleColleague{
    void send();
    void receive();
}

class SimpleConcreteColleagueA implements ISimpleColleague{
    SimpleConcreteColleagueA(){
        SimpleMediator mediator = SimpleMediator.getMedium();
        mediator.register(this);
    }
    @Override
    public void send() {
        SimpleMediator mediator = SimpleMediator.getMedium();
        System.out.println("Specific colleagues A Make a request!");
        mediator.relay(this);
    }

    @Override
    public void receive() {
        System.out.println("Specific colleagues A Request received!");
    }
}

class SimpleConcreteColleagueB implements ISimpleColleague{
    SimpleConcreteColleagueB(){
        SimpleMediator mediator = SimpleMediator.getMedium();
        mediator.register(this);
    }
    @Override
    public void send() {
        SimpleMediator mediator = SimpleMediator.getMedium();
        System.out.println("Specific colleagues B Make a request!");
        mediator.relay(this);
    }

    @Override
    public void receive() {
        System.out.println("Specific colleagues B Request received!");
    }
}
public class SimpleMediatorPatternTest {
    public static void main(String[] args){
        ISimpleColleague colleagueA = new SimpleConcreteColleagueA();
        ISimpleColleague colleagueB = new SimpleConcreteColleagueB();

        colleagueA.send();
        System.out.println("------------------------");
        colleagueB.send();
    }
}

4. Application of mediator mode in JDK source code

Open the Timer class in the JDK and check the structure of the Timer class. You will find many schedule() overloaded methods in the Timer class, as shown in the following figure:

Click any one of the methods and you will find that the private sched() method is called in the end. The source code is as follows:

public class Timer {
    ...
    public void schedule(TimerTask task, long delay) {
        if (delay < 0)
            throw new IllegalArgumentException("Negative delay.");
        sched(task, System.currentTimeMillis()+delay, 0);
    }
    ...

    private void sched(TimerTask task, long time, long period) {
        if (time < 0)
            throw new IllegalArgumentException("Illegal execution time.");

        // Constrain value of period sufficiently to prevent numeric
        // overflow while still being effectively infinitely large.
        if (Math.abs(period) > (Long.MAX_VALUE >> 1))
            period >>= 1;

        synchronized(queue) {
            if (!thread.newTasksMayBeScheduled)
                throw new IllegalStateException("Timer already cancelled.");

            synchronized(task.lock) {
                if (task.state != TimerTask.VIRGIN)
                    throw new IllegalStateException(
                        "Task already scheduled or cancelled");
                task.nextExecutionTime = time;
                task.period = period;
                task.state = TimerTask.SCHEDULED;
            }

            queue.add(task);
            if (queue.getMin() == task)
                queue.notify();
        }
    }
    ...
}

No matter what kind of tasks are added to the queue, we call the objects in the queue "colleagues". Looking at the source code of sched(), we can see that all tasks are placed in the task queue maintained by the Timer class, and the communication between colleagues is coordinated through the Timer. Therefore, the Timer assumes the role of an intermediary, and the tasks in the task queue are specific peers.

Topics: Design Pattern