Jump to content

Mediator: Difference between revisions

From Knowledge Base
Created page with "= Mediator Pattern = Category:PatternsCategory:Behavioral Design Patterns __TOC__ The '''Mediator Pattern''' (Behavioral) promotes loose coupling between objects by centralizing their interactions through a mediator object. center|alt=Mediator Plane "Everybody who had fish" == Benefits == * '''Decoupling''': Components can interact with each other through the mediator without needing direct references, reducing dependencies. * '..."
 
No edit summary
 
Line 206: Line 206:


'''Mediator Pattern:'''
'''Mediator Pattern:'''
# <font color="blue">Centralized</font> Communication: The Mediator acts as a central hub for communication, and components communicate through the mediator.
# '''Centralized Communication''': The Mediator acts as a central hub for communication, and components communicate through the mediator.
# <font color="blue">Reduced</font> Coupling: Components are decoupled from each other, as they do not need to maintain direct references to one another.
#Reduced Coupling: Components are decoupled from each other, as they do not need to maintain direct references to one another.
# <font color="blue">High</font> Scalability: Adding new components or changing interactions is easier, as it generally requires modifications to the mediator and does not impact existing components.
# '''High Scalability''': Adding new components or changing interactions is easier, as it generally requires modifications to the mediator and does not impact existing components.
# Complexity: The Mediator can become complex when managing multiple events and their subscribers.
# '''Complexity''': The Mediator can become complex when managing multiple events and their subscribers.


'''Observer Pattern:'''
'''Observer Pattern:'''
# Decentralized Communication: In the Observer Pattern, subjects and observers communicate directly with each other. Subjects maintain a list of their observers.
# '''Decentralized Communication''': In the Observer Pattern, subjects and observers communicate directly with each other. Subjects maintain a list of their observers.
# Tight Coupling: Subjects and observers have direct references to each other, leading to tighter coupling between components.
# '''Tight Coupling''': Subjects and observers have direct references to each other, leading to tighter coupling between components.
# Low Scalability: Adding new observers or subjects can be challenging, as it may require modifications to existing components.
# '''Low Scalability''': Adding new observers or subjects can be challenging, as it may require modifications to existing components.
# <font color="blue">Simplicity</font>: The Observer Pattern is straightforward and widely used for simple cases where you need to notify multiple objects about a change.
# '''Simplicity''': The Observer Pattern is straightforward and widely used for simple cases where you need to notify multiple objects about a change.


In summary, the choice between the Mediator Pattern and the Observer Pattern depends on the specific requirements of your application. The Mediator Pattern is useful when you want to achieve a high degree of decoupling and centralize communication, making it more suitable for larger and more complex systems. The Observer Pattern, on the other hand, is simpler and more suitable for scenarios where objects need to notify and listen for changes in a straightforward manner.
In summary, the choice between the Mediator Pattern and the Observer Pattern depends on the specific requirements of your application. The Mediator Pattern is useful when you want to achieve a high degree of decoupling and centralize communication, making it more suitable for larger and more complex systems. The Observer Pattern, on the other hand, is simpler and more suitable for scenarios where objects need to notify and listen for changes in a straightforward manner.

Latest revision as of 15:25, 17 January 2025

Mediator Pattern

The Mediator Pattern (Behavioral) promotes loose coupling between objects by centralizing their interactions through a mediator object.

Error creating thumbnail: Unable to save thumbnail to destination


Benefits

  • Decoupling: Components can interact with each other through the mediator without needing direct references, reducing dependencies.
  • Centralized Control: The mediator centralizes event handling, making it easier to add, remove, or modify how events are processed.
  • Flexibility: New components can be added that subscribe to events in the mediator without changing existing components.
  • Simplified Object Communication: The mediator handles all communication logic, simplifying the interaction between components.

Use Cases

  • Event Handling Systems: Where various components need to react to events without creating a web of interdependencies.
  • Chat Applications: Where the mediator can manage messages between different users.
  • Control Panels: In complex UIs, where actions in one part of the UI need to affect other parts without tight coupling between components.

How It Works

The Mediator Pattern typically involves the following components:

  • Mediator Interface: Defines the interface for communication with Colleague objects.
  • Concrete Mediator: Implements the Mediator interface and coordinates communication between Colleague objects.
  • Colleagues: A set of objects that perform their interaction with one another through the mediator.

Implementation Example

In this implementation example, the Mediator Pattern is used to decouple components. The pattern provides a way for objects to communicate through a central mediator without direct references to each other.

Step 1: Define the IMediator Interface

Create an interface that defines the methods for the mediator to handle event publishing and subscribing.

namespace MediatorPattern;
public interface IMediator {
    void Publish(string eventName, object eventArgs);
    void Subscribe(string eventName, Action<object> callback);  
}

Step 2: Implement the Mediator Class

Create a Mediator class that implements the IMediator interface. This class handles event publication and subscription.

namespace MediatorPattern;
public class Mediator : IMediator {
    private readonly Dictionary<string, List<Action<object>>> _eventCallbacks = new();
    public void Publish(string eventName, object eventArgs) {
        if (_eventCallbacks.ContainsKey(eventName)) {
            foreach (var callback in _eventCallbacks[eventName]) {
                callback(eventArgs);
            }
        }
    }
    public void Subscribe(string eventName, Action<object> callback) {
        if (!_eventCallbacks.ContainsKey(eventName)) {
            _eventCallbacks[eventName] = new List<Action<object>>();
        }
        _eventCallbacks[eventName].Add(callback);
    }
}

Step 3: Implement Components Subscribing to the Mediator

Design additional components that subscribe to specific events and define actions to be taken when those events occur. Here, we'll create a DataLogger and EmailNotifier.

namespace MediatorPattern;
public class DataLogger {
    public DataLogger(IMediator mediator) {
        mediator.Subscribe("DataInserted", (data) => {
            var insertedData = (Data)data;
            Console.WriteLine($"Data inserted: ID={insertedData.Id}, Name={insertedData.Name}");
        });
    }
}

public class EmailNotifier {
    public EmailNotifier(IMediator mediator) {
        mediator.Subscribe("DataUpdated", (data) => {
            var updatedData = (Data)data;
            Console.WriteLine($"Data updated: ID={updatedData.Id}, Name={updatedData.Name}");
        });
    }
}

Step 4: Use the Mediator

Now that we have defined the IMediator interface, implemented the Mediator class, and created components that subscribe to events, it's time to put it all together in a C# application.

namespace MediatorPattern;
class Program {
    static void Main(string[] args) {
        // Create the mediator
        IMediator mediator = new Mediator();

        // Create components that subscribe to events
        var dataLogger = new DataLogger(mediator);
        var emailNotifier = new EmailNotifier(mediator);

        // Simulate events
        var data1 = new Data (Guid.NewGuid(), "Data 1" );
        var data2 = new Data (Guid.NewGuid(), "Data 2" );

        // Publishing events
        mediator.Publish("DataInserted", data1);
        mediator.Publish("DataUpdated", data2);
    }
}

Class Diagram

Simple approach:


More complex:

}


Our implementation:

Comparison to Observer Pattern

The Mediator Pattern is often compared to the Observer Pattern, as both patterns deal with the communication between objects:

Mediator Pattern:

  1. Centralized Communication: The Mediator acts as a central hub for communication, and components communicate through the mediator.
  2. Reduced Coupling: Components are decoupled from each other, as they do not need to maintain direct references to one another.
  3. High Scalability: Adding new components or changing interactions is easier, as it generally requires modifications to the mediator and does not impact existing components.
  4. Complexity: The Mediator can become complex when managing multiple events and their subscribers.

Observer Pattern:

  1. Decentralized Communication: In the Observer Pattern, subjects and observers communicate directly with each other. Subjects maintain a list of their observers.
  2. Tight Coupling: Subjects and observers have direct references to each other, leading to tighter coupling between components.
  3. Low Scalability: Adding new observers or subjects can be challenging, as it may require modifications to existing components.
  4. Simplicity: The Observer Pattern is straightforward and widely used for simple cases where you need to notify multiple objects about a change.

In summary, the choice between the Mediator Pattern and the Observer Pattern depends on the specific requirements of your application. The Mediator Pattern is useful when you want to achieve a high degree of decoupling and centralize communication, making it more suitable for larger and more complex systems. The Observer Pattern, on the other hand, is simpler and more suitable for scenarios where objects need to notify and listen for changes in a straightforward manner.

MediatR package

MediatR is a popular open-source library for implementing the Mediator pattern in .NET applications, including EF Core-based applications (EF Core is Microsoft's default ORM for .NET).

The MediatR package facilitates several key functionalities:

  • Request and RequestHandler: A "Request" represents a specific action or query, with an associated "RequestHandler" for processing.
  • Command and CommandHandler: Commands are for actions with side effects, handled by "CommandHandlers."
  • Queries: Read-only requests for data retrieval, managed by "QueryHandlers."
  • Mediator: Acts as a message dispatcher to route requests to their handlers.
  • Notification Handling: Broadcasts notifications to multiple handlers for event-driven actions.
  • Pipeline Behaviors: Inserts middleware for logging, validation, or transaction management.
  • CQRS Support: Ideal for separating commands from queries in CQRS pattern applications.
  • Dependency Injection Friendly: Integrates with dependency injection frameworks for managing handler lifetimes.
  • Asynchronous Processing: Supports async handlers for scalable, non-blocking operations.

https://code-maze.com/cqrs-mediatr-in-aspnet-core/