Dr. Yan Hong's book "JAVA and Patterns" begins with a description of the Mediator pattern:
Mediator pattern is the behavior pattern of objects. The Mediator pattern wraps up the way a set of objects interact so that they don't have to explicitly reference each other. This allows them to be loosely coupled. When the interactions between some of these objects change, it does not immediately affect the interactions between other objects. This ensures that these interactions can vary independently of each other.
Why a mediator is needed
As shown in the figure below, there are a large number of objects in this schematic diagram. These objects can both affect other objects and be affected by other objects, so they are often called Colleague objects. These co-workers form the behavior of the system through their interaction with each other. As can be seen from the figure, almost every object needs to interact with other objects, and this interaction is manifested as a direct coupling between one object and another object. This is an overcoupled system.
By introducing the mediator object (Mediator), the network structure of the system can be transformed into a star structure centered on the mediator, as shown in the figure below. In this star structure, a colleague object no longer interacts with another object through a direct connection; instead, it interacts with another object through a mediator object. The existence of the mediator object ensures the stability of the object structure, that is to say, the system structure will not cause a lot of modification work due to the introduction of new objects.
A good object-oriented design can increase collaboration and reduce coupling between objects. A well-thought-out design breaks down a system into a group of cooperating co-worker objects, then gives each co-worker object unique responsibilities and appropriately configures the collaborative relationships between them so that they can work together.
If there is no motherboard
As we all know, the interaction between various accessories in the computer is mainly completed through the motherboard. If there is no motherboard in the computer, then the various accessories must interact with each other on their own to transmit data to each other. And because the interfaces of each accessory are different, when interacting with each other, the data interface must be converted to match.
Fortunately, with the motherboard, the interaction of various accessories is completely completed through the motherboard. Each accessory only needs to interact with the motherboard, and the motherboard knows how to deal with all the accessories, which makes it much simpler.
The structure of the mediator pattern
A schematic class diagram for the Mediator pattern is shown below:
Mediator mode includes the following roles:
● Abstract mediator (Mediator) role: Define the interface from the colleague object to the mediator object, in which the main method is one (or more) event methods.
● Concrete Mediator (ConcreteMediator) role: implements the event method declared by the abstract mediator. The specific mediator is aware of all specific colleague classes and is responsible for specifically coordinating the interaction between each colleague object.
● Abstract colleague class (Colleague) role: defines the interface from the mediator to the colleague object. Colleague objects only know about the mediator and not the other collegial objects.
● ConcreteColleague role: All concrete colleague classes inherit from the abstract colleague class. To implement your own business, when you need to communicate with other colleagues, communicate with the mediator who holds it. The mediator will be responsible for interacting with other colleagues.
source code
The abstract mediator class copy code is as follows:
public interface Mediator {
/**
* The colleague object notifies the mediator method when it changes itself
* Let the mediator be responsible for the corresponding interactions with other colleague objects
*/
public void changed(Colleague c);
}
concrete mediator class
Copy the code code as follows:
public class ConcreteMediator implements Mediator {
//Hold and maintain colleague A
private ConcreteColleagueA colleagueA;
//Hold and maintain colleague B
private ConcreteColleagueB colleagueB;
public void setColleagueA(ConcreteColleagueA colleagueA) {
this.colleagueA = colleagueA;
}
public void setColleagueB(ConcreteColleagueB colleagueB) {
this.colleagueB = colleagueB;
}
@Override
public void changed(Colleague c) {
/**
* A certain colleague class has changed, usually requiring interaction with other colleagues
* Specifically coordinate the corresponding colleague objects to achieve collaborative behavior
*/
}
}
abstract colleague class
Copy the code code as follows:
public abstract class Colleague {
//Hold a mediator object
private Mediator mediator;
/**
*Constructor
*/
public Colleague(Mediator mediator){
this.mediator = mediator;
}
/**
* Get the mediator object corresponding to the current colleague class
*/
public Mediator getMediator() {
return mediator;
}
}
The specific code for copying the same class is as follows:
public class ConcreteColleagueA extends Colleague {
public ConcreteColleagueA(Mediator mediator) {
super(mediator);
}
/**
* Indicate methods to perform certain operations
*/
public void operation(){
//Notify the mediator object when you need to communicate with other colleagues
getMediator().changed(this);
}
}
Copy the code code as follows:
public class ConcreteColleagueB extends Colleague {
public ConcreteColleagueB(Mediator mediator) {
super(mediator);
}
/**
* Indicate methods to perform certain operations
*/
public void operation(){
//Notify the mediator object when you need to communicate with other colleagues
getMediator().changed(this);
}
}
Use your computer to watch movies
In daily life, we often use computers to watch movies. Let’s describe this process. After simplification, we assume that there will be the following interactive process:
(1) First, the optical drive needs to read the data on the optical disc, and then tell the motherboard that its status has changed.
(2) The motherboard obtains the data from the optical drive and hands the data to the CPU for analysis and processing.
(3) After the CPU finishes processing, it divides the data into video data and audio data, and notifies the motherboard that it has finished processing.
(4) The motherboard obtains the data processed by the CPU and passes the data to the graphics card and sound card respectively to display the video and make sounds.
To implement the example using the mediator pattern, it is necessary to distinguish between colleague objects and mediator objects. Obviously, the motherboard is the mediator, and accessories such as optical drives, sound cards, CPUs, and graphics cards are all colleagues.
source code
The abstract colleague class copy code is as follows:
public abstract class Colleague {
//Hold a mediator object
private Mediator mediator;
/**
*Constructor
*/
public Colleague(Mediator mediator){
this.mediator = mediator;
}
/**
* Get the mediator object corresponding to the current colleague class
*/
public Mediator getMediator() {
return mediator;
}
}
Colleague class - optical drive copy code code is as follows:
public class CDDriver extends Colleague{
//Data read from the optical drive
private String data = "";
/**
*Constructor
*/
public CDDriver(Mediator mediator) {
super(mediator);
}
/**
* Get the data read from the disc
*/
public String getData() {
return data;
}
/**
* Read CD
*/
public void readCD(){
//Before the comma is the data displayed in the video, and after the comma is the sound
this.data = "One Piece, I am determined to be the Pirate King";
//Notify the motherboard that its status has changed
getMediator().changed(this);
}
}
Colleague class——CPU
Copy the code code as follows:
public class CPU extends Colleague {
//Decomposed video data
private String videoData = "";
//Decomposed sound data
private String soundData = "";
/**
*Constructor
*/
public CPU(Mediator mediator) {
super(mediator);
}
/**
* Get decomposed video data
*/
public String getVideoData() {
return videoData;
}
/**
* Get the decomposed sound data
*/
public String getSoundData() {
return soundData;
}
/**
* Process the data and divide the data into audio and video data
*/
public void executeData(String data){
//Decompose the data. The front is video data and the back is audio data.
String[] array = data.split(",");
this.videoData = array[0];
this.soundData = array[1];
//Notify the motherboard that the CPU has completed the work
getMediator().changed(this);
}
}
Colleague class - graphics card copy code code is as follows:
public class VideoCard extends Colleague {
/**
*Constructor
*/
public VideoCard(Mediator mediator) {
super(mediator);
}
/**
* Display video data
*/
public void showData(String data){
System.out.println("You are watching: " + data);
}
}
Colleague class - sound card copy code code is as follows:
public class SoundCard extends Colleague {
/**
*Constructor
*/
public SoundCard(Mediator mediator) {
super(mediator);
}
/**
* Make sounds based on audio data
*/
public void soundData(String data){
System.out.println("Voiceover: " + data);
}
}
The abstract mediator class copy code is as follows:
public interface Mediator {
/**
* The colleague object notifies the mediator method when it changes.
* Let the mediator be responsible for the corresponding interactions with other colleague objects
*/
public void changed(Colleague c);
}
The specific mediator class copy code is as follows:
public class MainBoard implements Mediator {
//Need to know the colleague class to interact with-the optical drive class
private CDDriver cdDriver = null;
//Need to know the colleague class to interact with-CPU class
private CPU cpu = null;
//Need to know the class of colleagues to interact with - the graphics card class
private VideoCard videoCard = null;
//Need to know the class of colleagues to interact with - the sound card class
private SoundCard soundCard = null;
public void setCdDriver(CDDriver cdDriver) {
this.cdDriver = cdDriver;
}
public void setCpu(CPU cpu) {
this.cpu = cpu;
}
public void setVideoCard(VideoCard videoCard) {
this.videoCard = videoCard;
}
public void setSoundCard(SoundCard soundCard) {
this.soundCard = soundCard;
}
@Override
public void changed(Colleague c) {
if(c instanceof CDDriver){
//Indicates that the optical drive has read data
this.opeCDDriverReadData((CDDriver)c);
}else if(c instanceof CPU){
this.opeCPU((CPU)c);
}
}
/**
* Handle the interaction with other objects after the optical drive reads data
*/
private void opeCDDriverReadData(CDDriver cd){
//First get the data read by the optical drive
String data = cd.getData();
//Transfer these data to the CPU for processing
cpu.executeData(data);
}
/**
* Handle the interaction between the CPU and other objects after processing the data
*/
private void opeCPU(CPU cpu){
//Get the data processed by the CPU first
String videoData = cpu.getVideoData();
String soundData = cpu.getSoundData();
//Transfer these data to the graphics card and sound card for display
videoCard.showData(videoData);
soundCard.soundData(soundData);
}
}
The client class copy code is as follows:
public class Client {
public static void main(String[] args) {
//Create mediator - motherboard
MainBoard mediator = new MainBoard();
//Create colleague class
CDDriver cd = new CDDriver(mediator);
CPU cpu = new CPU(mediator);
VideoCard vc = new VideoCard(mediator);
SoundCard sc = new SoundCard(mediator);
//Let the mediator know all colleagues
mediator.setCdDriver(cd);
mediator.setCpu(cpu);
mediator.setVideoCard(vc);
mediator.setSoundCard(sc);
//Start watching the movie, put the disc into the optical drive, and the optical drive starts reading the disc
cd.readCD();
}
}
The running results are as follows:
Advantages of the Mediator Pattern
● loose coupling
The mediator pattern encapsulates the interactions between multiple colleague objects into the mediator object, thereby loosely coupling the colleague objects and basically achieving complementary dependencies. In this way, colleague objects can be changed and reused independently, instead of "moving one place and affecting the whole body" as before.
● Centralized control of interactions
The interactions of multiple colleague objects are encapsulated in the mediator object for centralized management, so that when these interactive behaviors change, you only need to modify the mediator object. Of course, if it is an already completed system, then expand the mediator Object, and each colleague class does not need to be modified.
● Many-to-many becomes one-to-many
When the mediator pattern is not used, the relationship between colleague objects is usually many-to-many. After the mediator object is introduced, the relationship between the mediator object and the colleague object usually becomes a two-way one-to-many, which makes the relationship between the objects Easier to understand and implement.
Disadvantages of the Mediator Pattern
One potential drawback of the mediator model is over-centralization. If the interaction of colleague objects is very large and complex, when all these complexities are concentrated on the mediator, the mediator object will become very complex and difficult to manage and maintain.