Mediator pattern

Last updated

In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program's running behavior.

Contents

In object-oriented programming, programs often consist of many classes. Business logic and computation are distributed among these classes. However, as more classes are added to a program, especially during maintenance and/or refactoring, the problem of communication between these classes may become more complex. This makes the program harder to read and maintain. Furthermore, it can become difficult to change the program, since any change may affect code in several other classes.

With the mediator pattern, communication between objects is encapsulated within a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces the dependencies between communicating objects, thereby reducing coupling.

Overview

The mediator [1] design pattern is one of the twenty-three well-known design patterns that describe how to solve recurring design problems to design flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, and reuse.

Problems that the mediator design pattern can solve [2]

Defining a set of interacting objects by accessing and updating each other directly is inflexible because it tightly couples the objects to each other and makes it impossible to change the interaction independently from (without having to change) the objects. And it stops the objects from being reusable and makes them hard to test.

Tightly coupled objects are hard to implement, change, test, and reuse because they refer to and know about many different objects.

Solutions described by the mediator design pattern

The objects interact with each other indirectly through a mediator object that controls and coordinates the interaction.

This makes the objects loosely coupled. They only refer to and know about their mediator object and have no explicit knowledge of each other.

See also the UML class and sequence diagram below.

Definition

The essence of the mediator pattern is to "define an object that encapsulates how a set of objects interact". It promotes loose coupling by keeping objects from referring to each other explicitly, and it allows their interaction to be varied independently. [3] [4] Client classes can use the mediator to send messages to other clients, and can receive messages from other clients via an event on the mediator class.

Structure

UML class and sequence diagram

A sample UML class and sequence diagram for the mediator design pattern. W3sDesign Mediator Design Pattern UML.jpg
A sample UML class and sequence diagram for the mediator design pattern.

In the above UML class diagram, the Colleague1 and Colleague2 classes do not refer to (and update) each other directly. Instead, they refer to the common Mediator interface for controlling and coordinating interaction (mediate()), which makes them independent from one another with respect to how the interaction is carried out. The Mediator1 class implements the interaction between Colleague1 and Colleague2.

The UML sequence diagram shows the run-time interactions. In this example, a Mediator1 object mediates (controls and coordinates) the interaction between Colleague1 and Colleague2 objects.

Assuming that Colleague1 wants to interact with Colleague2 (to update/synchronize its state, for example), Colleague1 calls mediate(this) on the Mediator1 object, which gets the changed data from Colleague1 and performs an action2() on Colleague2.

Thereafter, Colleague2 calls mediate(this) on the Mediator1 object, which gets the changed data from Colleague2 and performs an action1() on Colleague1.

Class diagram

The mediator behavioural design pattern Mediator design pattern.png
The mediator behavioural design pattern
Participants

Mediator - defines the interface for communication between Colleague objects

ConcreteMediator - implements the mediator interface and coordinates communication between Colleague objects. It is aware of all of the Colleagues and their purposes with regards to inter-communication.

Colleague - defines the interface for communication with other Colleagues through its Mediator

ConcreteColleague - implements the Colleague interface and communicates with other Colleagues through its Mediator

Example

C#

The mediator pattern ensures that components are loosely coupled, such that they do not call each other explicitly, but instead do so through calls to a mediator. In the following example, the mediator registers all Components and then calls their SetState methods.

interfaceIComponent{voidSetState(objectstate);}classComponent1:IComponent{internalvoidSetState(objectstate){thrownewNotImplementedException();}}classComponent2:IComponent{internalvoidSetState(objectstate){thrownewNotImplementedException();}}// Mediates the common tasksclassMediator{internalIComponentComponent1{get;set;}internalIComponentComponent2{get;set;}internalvoidChangeState(objectstate){this.Component1.SetState(state);this.Component2.SetState(state);}}

A chat room could use the mediator pattern, or a system where many ‘clients’ each receive a message each time one of the other clients performs an action (for chat rooms, this would be when each person sends a message). In reality using the mediator pattern for a chat room would only be practical when used with remoting. Using raw sockets would not allow for the delegate callbacks (people subscribed to the Mediator class’ MessageReceived event).

publicdelegatevoidMessageReceivedEventHandler(stringmessage,stringsender);publicclassMediator{publiceventMessageReceivedEventHandlerMessageReceived;publicvoidSend(stringmessage,stringsender){if(MessageReceived!=null){Console.WriteLine("Sending '{0}' from {1}",message,sender);MessageReceived(message,sender);}}}publicclassPerson{privateMediator_mediator;publicstringName{get;set;}publicPerson(Mediatormediator,stringname){Name=name;_mediator=mediator;_mediator.MessageReceived+=newMessageReceivedEventHandler(Receive);}privatevoidReceive(stringmessage,stringsender){if(sender!=Name)Console.WriteLine("{0} received '{1}' from {2}",Name,message,sender);}publicvoidSend(stringmessage){_mediator.Send(message,Name);}}

Java

In the following example, a Mediator object controls the values of several Storage objects, forcing the user code to access the stored values through the mediator. When a storage object wants to emit an event indicating that its value has changed, it also goes back to the mediator object (via the method notifyObservers) that controls the list of the observers (implemented using the observer pattern).

importjava.util.HashMap;importjava.util.Optional;importjava.util.concurrent.CopyOnWriteArrayList;importjava.util.function.Consumer;classStorage<T>{Tvalue;TgetValue(){returnvalue;}voidsetValue(Mediator<T>mediator,StringstorageName,Tvalue){this.value=value;mediator.notifyObservers(storageName);}}classMediator<T>{privatefinalHashMap<String,Storage<T>>storageMap=newHashMap<>();privatefinalCopyOnWriteArrayList<Consumer<String>>observers=newCopyOnWriteArrayList<>();publicvoidsetValue(StringstorageName,Tvalue){Storagestorage=storageMap.computeIfAbsent(storageName,name->newStorage<>());storage.setValue(this,storageName,value);}publicOptional<T>getValue(StringstorageName){returnOptional.ofNullable(storageMap.get(storageName)).map(Storage::getValue);}publicvoidaddObserver(StringstorageName,Runnableobserver){observers.add(eventName->{if(eventName.equals(storageName)){observer.run();}});}voidnotifyObservers(StringeventName){observers.forEach(observer->observer.accept(eventName));}}publicclassMediatorDemo{publicstaticvoidmain(String[]args){Mediator<Integer>mediator=newMediator<>();mediator.setValue("bob",20);mediator.setValue("alice",24);mediator.getValue("alice").ifPresent(age->System.out.println("age for alice: "+age));mediator.addObserver("bob",()->{System.out.println("new age for bob: "+mediator.getValue("bob").orElseThrow(RuntimeException::new));});mediator.setValue("bob",21);}}

See also

Related Research Articles

In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying the structures. It is one way to follow the open/closed principle.

Java Platform, Standard Edition is a computing platform for development and deployment of portable code for desktop and server environments. Java SE was formerly known as Java 2 Platform, Standard Edition (J2SE).

In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.

The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently", introduced by the Gang of Four. The bridge uses encapsulation, aggregation, and can use inheritance to separate responsibilities into different classes.

<span class="mw-page-title-main">Flyweight pattern</span> Software design pattern for objects

In computer programming, the flyweight software design pattern refers to an object that minimizes memory usage by sharing some of its data with other similar objects. The flyweight pattern is one of twenty-three well-known GoF design patterns. These patterns promote flexible object-oriented software design, which is easier to implement, change, test, and reuse.

The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. The intent of the builder design pattern is to separate the construction of a complex object from its representation. It is one of the Gang of Four design patterns.

In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern as well as to the Open-Closed Principle, by allowing the functionality of a class to be extended without being modified. Decorator use can be more efficient than subclassing, because an object's behavior can be augmented without defining an entirely new object.

In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements. The iterator pattern decouples algorithms from containers; in some cases, algorithms are necessarily container-specific and thus cannot be decoupled.

In software design and engineering, the observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

In computer programming, the strategy pattern is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.

In computing, type introspection is the ability of a program to examine the type or properties of an object at runtime. Some programming languages possess this capability.

In computing based on the Java Platform, JavaBeans is a technology developed by Sun Microsystems and released in 1996, as part of JDK 1.1.

<span class="mw-page-title-main">Dependency injection</span> Software programming technique

In software engineering, dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on. A form of inversion of control, dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs. The pattern ensures that an object or function which wants to use a given service should not have to know how to construct those services. Instead, the receiving 'client' is provided with its dependencies by external code, which it is not aware of. Dependency injection helps by making implicit dependencies explicit and helps solve the following problems:

In the Java computer programming language, an annotation is a form of syntactic metadata that can be added to Java source code. Classes, methods, variables, parameters and Java packages may be annotated. Like Javadoc tags, Java annotations can be read from source files. Unlike Javadoc tags, Java annotations can also be embedded in and read from Java class files generated by the Java compiler. This allows annotations to be retained by the Java virtual machine at run-time and read via reflection. It is possible to create meta-annotations out of the existing ones in Java.

Event-driven architecture (EDA) is a software architecture paradigm promoting the production, detection, consumption of, and reaction to events.

A delegate is a form of type-safe function pointer used by the Common Language Infrastructure (CLI). Delegates specify a method to call and optionally an object to call the method on. Delegates are used, among other things, to implement callbacks and event listeners. A delegate object encapsulates a reference to a method. The delegate object can then be passed to code that can call the referenced method, without having to know at compile time which method will be invoked.

In programming and software design, an event is an action or occurrence recognized by software, often originating asynchronously from the external environment, that may be handled by the software. Computer events can be generated or triggered by the system, by the user, or in other ways. Typically, events are handled synchronously with the program flow; that is, the software may have one or more dedicated places where events are handled, frequently an event loop.

In object-oriented design, the chain-of-responsibility pattern is a behavioral design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.

The Archetype pattern separates the logic from implementation; the separation is accomplished by there being two abstract classes, a decorator and a delegate. The Factory handles the mapping of decorator and delegate classes and returns the pair associated with a parameter or parameters passed. The interface is the contract between a decorator, a delegate and the calling class creating an Inversion of Responsibility. This example use two branches however you can have 'N' branches as required. The pattern means that one branch from the interface does not have to worry about how another branch operators as long it implements the interface.

References

  1. Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software . Addison Wesley. pp.  273ff. ISBN   0-201-63361-2.{{cite book}}: CS1 maint: multiple names: authors list (link)
  2. Franke, Günther. "The Mediator design pattern - Problem, Solution, and Applicability". w3sDesign. Retrieved 2017-08-12.
  3. Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Design Patterns . Addison-Wesley. ISBN   0-201-63361-2.
  4. "Mediator Design Pattern". SourceMaking.
  5. Franke, Günther. "The Mediator design pattern - Structure and Collaboration". w3sDesign. Retrieved 2017-08-12.