Chapter 20. The Mediator Pattern

When a program consists of a number of isolated classes, the logic and computation is divided among these classes. However, as more of these isolated classes are developed in the program, communication between them becomesmore complex. The more that each class needs to know about themethods ofanother class, the more tangled the class structure can become, therebymaking the program harder to read and harder to maintain.Further, itcan become difficult to change the program, since any changemight affect code in several other classes. The Mediator pattern addresses this problem by promoting looser coupling between these classes. Itaccomplishes this by beingthe only class that has detailed knowledge of themethods of other classes. Classes inform the Mediator when changes occur, and the Mediator then passes them on to any other classes that need to be informed.

An Example System

Consider a program with several buttons, two list boxes, and a text entry field for selecting a few names from a list, as shown in Figure 20.1.

A program with two lists, two buttons, and a text field, all of which will interact.

Figure 20.1. A program with two lists, two buttons, and a text field, all of which will interact.

When the program starts, the Copy and Clear buttons are disabled.

  1. When you select one of the names in the left-hand box, it is copied into the text field for editing, and the Copy button is enabled.

  2. When you click on the Copy button, that selected name is added to the right-hand list box, and the Clear button is enabled, as shown in Figure 20.2.

    Selecting a name enables the buttons, and clicking on the Copy button copies the name to the right-hand list box.

    Figure 20.2. Selecting a name enables the buttons, and clicking on the Copy button copies the name to the right-hand list box.

  3. When you click on the Clear button, the right-hand list box and the text fieldare cleared, the list box is deselected, and the two buttons are again disabled.

User interfaces such as this one are commonly used to select lists of people or products from lists. They often are more complicated, including insert, delete, and undo operations as well.

Interactions between Controls

The interactions between the visual controls are pretty complex, even in this simple example. Each visual object needs to know about two or more others. This can lead to quite a tangled relationship diagram, as shown in Figure 20.3.

A tangled web of interactions between classes in the simple visual interface we presented in Figures 20.1 and 20.2.

Figure 20.3. A tangled web of interactions between classes in the simple visual interface we presented in Figures 20.1 and 20.2.

The Mediator pattern simplifies this system by being the only class that is aware of the other classes in the system. Each control that the Mediator communicates with is called a Colleague. Each Colleague informs the Mediator when it has received a user event, and the Mediator decides which other classes should beinformed of this event. This simpler interaction scheme is illustrated in Figure20.4.

A Mediator class simplifies the interactions between classes.

Figure 20.4. A Mediator class simplifies the interactions between classes.

The advantage of the Mediator is clear: It is the only class that knows of the other classes and thus the only one that needs to be changed when one of the other classes changes or when other interface control classes are added.

Sample Code

Let's consider this program in detail and decide how each control is constructed. The main difference in writing a program using a Mediator class is that each class needs to be aware of the existence of the Mediator. You start by creating an instance of the Mediator and then pass the instance of the Mediator to each class in its constructor.

    Mediator med = new Mediator();
    kidList = new Kidlist( med);
    tx = new KTestField(med);

    Move = new MoveButton(this, med);
    Clear = new ClearButton(this, med);
    med.init();

We created new classes for each control, each derived from base classes, so we can handle the mediator operations within each class. The two buttons use the Command pattern and register themselves with the Mediator during their initializations. Following is the Copy button; the Clear button is analogous:

public class CopyButton extends JButton
implements Command {
    private Mediator med:          //copy of the Mediator
    public CopyButton(ActionListener fr, 
                        Mediator md) {
        super("Copy");             //create the button
        addActionListener(fr);     //add its listener
        med = md;                  //copy in the Mediator instance
        med.registerMove(this);    //register with the Mediator
    }
    public void Execute() {        //execute the copy
        med.Move();
    }
}

The Kid name list is based on that used in the last two examples but is expanded so that the data loading of the list and the registering of the list with the Mediator both occur in the constructor. In addition, we make the enclosing class the ListSelectionListener and pass the click on any list item on to the Mediator directly from this class.

public class KidList extends JawtList
implements ListSelectionListener {
    private KidData kdata;
    private Mediator med;
    public KidList(Mediator md) {
        super(20);
        kdata = new KidData ("50free.txt"0;
        fillKidList();
        med = md;
        med.registerKidList(this);
        addListSelectionListener(this);
    }
    //--------------
    public void valueChanged(ListSelectionEvent ls) {
        JList obj = (JList)ls.getSource();
        if (obj.getSelectedIndex() >= 0)
            med.select();
    }
    //--------------
    private void fillKidList() {
        Enumeration ekid = kdata.elements();
        while (ekid.hasMoreElements()) {
            Kid k =(Kid)ekid.nextElement();
            add(k.getFrname()+" "+k.getLname());
        }
    }
}

The text field is even simpler, since all it does is register itself with the Mediator.

public class KTextField extends JTextField {
    private Mediator med;
    public KTextField(Mediator md) {
        super(10);
        med = md;
        med.registerText(this);
    }
}

The general point of all of these classes is that each knows about the Mediator and tells the Mediator of its existence so that the Mediator can send commands to it when appropriate.

The Mediator itself is very simple. It supports the move, clear, and select methods and has register methods for each control.

private class Mediator {
    private ClearButton       clearButton;
    private MoveButton        moveButton;
    private KTextField        ktext;
    private KidList           klist;
    private PickedKidsList    picked;
    public void move() {
        picked.add(ktext.getText();
        clearButton.setEnabled(true);
    }
    //--------------
    public void init() {
        Clear();
    }
    //--------------
    public void clear() {
        ktext.setText("");
        moveButton.setEnabled(false);
        clearButton.setEnabled(false);
        picked.clear();
        klist.clearSelection();
    }
    //--------------
    public void select() {
        String s = (String)klist.getSelectedValue();
        ktext.setText(s);
        moveButton.setEnabled(true);
    }
    //--------------
    public void registerClear(ClearButton cb) {
        clearButton = cb;
    }
    //--------------
    public void registerMove(MoveButton mv) {
        moveButton = mv;
    }
    //--------------
    public void registerText(KTextField tx) {
        ktext = tx;
    }
    //--------------
    public void registerPicked(PickedKidsList pl) {
        picked = p1;
    }
    //--------------
    public void registerKidList(KidList kl) {
        klist = kl;
    }
}

Initialization of the System

One further operation that is best delegated to the Mediator is the initialization of all of the controls to the desired state. When we launch the program, each control must be in a known, default state. Because these states might change as the program evolves, we create in the Mediator an init method that sets them all to the desired state. In this case, that state is the same as that achieved by clicking on the Clear button.

public void init() {
    clear();
}

Mediators and Command Objects

The two buttons in the program are Command objects. We register the main user interface frame as the ActionListener when we initialize them. As noted in Chapter 17, this makes processing the button click events simple.

public void actionPerformed(ActionEvent e) {
        Command comd = (Command)e.getSource();
        comd.Execute();
    }

Alternatively, we could register each derived class as its own listener and pass the results directly to the Mediator.

In either case, however, this represents the solution to one of the problems we noted in the discussion of the Command pattern in Chapter 17. That is, each button needs knowledge of many of the other user interface classes in order to execute its command. Here, we delegate that knowledge to the Mediator so that the Command buttons do not need any knowledge of the methods of the other visual objects. Figure 20.5 shows the class diagram for this program, illustrating both the Mediator pattern and the Command pattern.

The interactions between Command objects and the Mediator object.

Figure 20.5. The interactions between Command objects and the Mediator object.

Consequences of the Mediator Pattern

Using the Mediator pattern has the following consequences:

  1. The Mediator pattern keeps classes from becoming entangled when actions in one class need to be reflected in the state of another.

  2. Using a Mediator makes it easy to change a program's behavior. For many kinds of changes, you can merely change or subclass the Mediator and leave the rest of the program unchanged.

  3. New controls or other classes can be added without changing anything except the Mediator.

  4. The Mediator solves the problem of each Command object's needing to know too much about the objects and methods in the rest of a user interface.

  5. Sometimes, however, the Mediator can become a "god class," having too much knowledge of the rest of the program. This can make the Mediator hard to change and maintain. You can improve this situation by putting more of the function into the individual classes and less into the Mediator. Each object should carry out its own tasks, and the Mediator should manage only the interaction between objects.

  6. Each Mediator is a custom-written class that has methods for each Colleague to call and knows the methods that each Colleague has available. This makes it difficult to reuse Mediator code in different projects. However, most Mediators are quite simple, and writing this code is far easier than managing the complex object interaction in any other way.

Single Interface Mediators

The Mediator pattern described here acts as a kind of Observer pattern, observing changes in each of the Colleague elements, with each element having a custom interface to the Mediator. Another approach is to have a single interface to the Mediator and pass to it various objects that tell the Mediator which operations to perform.

In this approach, we avoid registering the active components and create a single action method with different polymorphic arguments for each of the action elements.

public void action(MoveButton mv){}
public void action(ClearButton clrButton){}
public void action(KidList klst){}

Thus we need not register the action objects, such as the buttons and source list boxes, since we can pass them as part of generic action methods.

In the same fashion, we can have a single Colleague interface that each Colleague implements, and each Colleague then decides what operation it is to carry out.

Implementation Issues

Mediators are not limited to use in visual interface programs, although that istheir most common application. You can use them whenever you are faced with the problem of resolving complex intercommunication between multiple objects.

Programs on the CD-ROM

Program

Description

MediatorMedDemo.java

Demonstrates the Mediator mediating between two buttons, a text field, and two list boxes.

MediatorSIMediatorSIMedDemo.java

Demonstates the same program but with a single action interface to the Mediator.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset