image

We use a computer that requires CPU, memory, graphics card, keyboard, monitor and other components to call each other to complete any function. If these components call each other directly, the relationship between them may be as shown in the picture below, which is very messy and complicated:

image

But computer developers did not let these parts directly call each other, but through the motherboard to unify coordination, so that each part only need to complete the function in accordance with the motherboard interface requirements, and then the processing completed data transfer to the motherboard can, do not need to understand other parts, at this time the structure is as follows:

image

Object-oriented design encourages the distribution of behavior among objects. This distribution can result in many connections between objects. In the worst case, every object knows about every other object. Although partitioning a system into many objects generally enhances reusability, the proliferation of interconnections between objects reduces reusability. The sheer number of interconnections makes it seem unlikely that one object can work without the support of other objects — the system appears as an indivisible whole. It is also difficult to make any significant changes to the system’s behavior, because the behavior is distributed among many objects. As a result, you may have to define many subclasses to customize the behavior of the system. Today’s mediator model was born to solve this problem.


define

A mediation object encapsulates a set of object interactions. The mediator loosens the coupling by eliminating the need for objects to explicitly refer to each other, and can change their interactions independently.

The dilemma solved by the mediator pattern is the tight coupling caused by the mutual reference of multiple objects. By introducing a mediator, the objects that used to refer to each other become colleague classes, which now have no relationship with each other and only interact with the mediator, thus achieving decoupling.

In this way, if you later add or modify the functionality of any colleague classes, you only need to modify the mediator, not the other colleague classes.

In plain English, the mediator pattern reduces coupling by converting many-to-many between objects to one-to-many.


UML diagrams and instructions

image

The figure above is the standard intermediary mode, which is actually used with a certain degree of distortion. To illustrate:

1. Whether mediator interface is required

Interfaces are used to implement interface-oriented programming, encapsulating the changes that come with multiple intermediary implementations. Therefore, if there are more than one mediator, the Mediator interface is needed; otherwise, it is not needed. In practical scenarios, there are rarely more than one mediator, so the interface is not needed

2, whether you need to define the parent of the colleague class

In practice, these colleague classes cannot be abstracted into the same parent class, and they have very little in common. So there’s no need to define a common parent class for them

3. Whether a colleague and mediator need to hold each other

Because a colleague and mediator need to notify each other of their changes for the other to respond, they are mutually owned in a standard implementation. It is possible to make Mediator a singleton and let colleagues call it directly.

In the same way, mediators do not need to hold a colleague. A colleague can be passed to a Mediator through the method argument.

4. Does mediator only need to provide a public method

In real development, we need to distinguish not only which peer class is passing the information, but also different business types, so we need to define multiple common methods based on actual requirements.

5. How do mediators and colleagues communicate

In the standard mode, you refer to each other and tell each other, but you can actually use the observer mode, where you have two people watching each other, and you can tell each other if something changes


Application of actual scene

1. Demand analysis

Suppose we use a computer to play a video and break it down into the following steps

  1. The drive reads the contents of the disc and passes the read contents to the motherboard
  2. The motherboard gets the content and gives it to the CPU
  3. After processing, the CPU passes the processed data to the motherboard
  4. The motherboard passes the data to the video card, which displays the video (ignoring the display step)

If the mediator pattern (join the motherboard) is not applicable, then each object (part) needs to reference each other, increasing coupling and making it difficult to reuse changes.

3. Code implementation

Define three colleague classes: CPU, CDDriver, and videoCard

#import <Foundation/Foundation.h> @interface CPU : NSObject -(void)executeData:(NSMutableString *)data; @end ====== #import "CPU.h" #import "mainBoard.h" @implementation CPU -(void )executeData:(NSMutableString *)data{ [data AppendString :@"+ processed by CPU "]; [[mainBoard shareInstance] handleData:data dataSource:self]; } @endCopy the code
#import "cddriver. h" #import "mainBoard. H" @implementation CDDriver -(void)readCD{NSString *data = @"BBC earth exploration journey "; NSMutableString *mStr = [[NSMutableString alloc]initWithString:data]; [[mainBoard shareInstance] handleData:mStr dataSource:self]; } @endCopy the code
#import "VideoCard.h" @implementation VideoCard -(void )executeData:(NSMutableString *)data{ [data AppendString :@"+ processed by video card "]; NSLog(@" Start playing video: "%@",data); } @endCopy the code

Define the mediator class

#import <Foundation/Foundation.h> @interface mainBoard : NSObject +(instancetype)shareInstance; -(void)handleData:(NSMutableString *)data dataSource:(id)source; @end ======================= #import "mainBoard.h" #import "CPU.h" #import "CDDriver.h" #import "VideoCard.h" static mainBoard *instance = nil; @implementation mainBoard +(instancetype)shareInstance{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if(instance == nil){ instance = [[self alloc]init]; }}); return instance; } -(void)handleData:(NSMutableString *)data dataSource:(id)source{ if ([source isKindOfClass:[CDDriver class]]){ CPU *cpu = [CPU new]; [cpu executeData:data]; }else if ([source isKindOfClass:[CPU class]]){ VideoCard *video = [VideoCard new]; ; } } @endCopy the code

Client call:



#import <Foundation/Foundation.h>
#import "CDDriver.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        CDDriver *cd = [CDDriver new];
        [cd  readCD];

    }
    return 0;
}Copy the code

The mainboard example shows that the three peers are only the mediator class, which are unaware of each other, reducing the coupling. This is just one example of what these three classes can do, but the reality is that they can do many things, such as playing audio and viewing documents. Each function requires interaction between these three classes, so wherever these functions are needed, these three classes need to be introduced. The more places these three classes are introduced, the more they will be coupled with other classes.

Assuming that these three classes need to be changed, all other references to these classes need to be changed, but if the mediator pattern is introduced, there are no problems.


The advantages and disadvantages

1. Reduce subclass generation of Mediators, which centralize behavior that would otherwise be distributed among multiple objects. To change these lines to, simply subclass Meditor. This way the Colleage classes can be reused.

2. It facilitates the loose coupling of Colleague decoupling mediators among Coleague. You can make and repeat the Colleague and Mediator classes independently.

3. It simplified Mediator and Colleague for many-to-many communication. One-to-many relationships are easier to understand, maintain, and extend.

4. It abstracts how objects collaborate by encapsulating mediations as a separate concept within an object, allowing you to shift your attention from the behavior of the objects themselves to the interaction between them. This helps to figure out how objects in a system interact.

5. It centralizes control. The mediator pattern turns the complexity of the interaction into the complexity of the mediator. Because the broker encapsulates the protocol, it can become more complex than any colleague. This can make intermediaries themselves behemoths that are hard to maintain.


When to use

  • A set of objects communicates in a well-defined but complex way. The resulting interdependencies are unstructured and difficult to understand.

  • An object refers to many other objects and communicates directly with them, making it difficult to reuse the object.

  • You want to customize a behavior that is distributed across multiple classes without generating too many subclasses.


Download the Demo

Mediator pattern Demo