mirror of
https://github.com/rjNemo/design-patterns
synced 2026-06-06 02:26:40 +00:00
Mediator (#18)
* doc: add documentation * doc: edit general documentation * add code example
This commit is contained in:
parent
a4cc9340b7
commit
edcd5d35d7
9 changed files with 141 additions and 2 deletions
|
|
@ -1,7 +1,5 @@
|
|||
# Design Patterns in Python
|
||||
|
||||
- [Link](https://refactoring.guru/design-patterns/catalog)
|
||||
|
||||
Design patterns are typical solutions to common problems
|
||||
in software design. Each pattern is like a blueprint
|
||||
that you can customize to solve a particular
|
||||
|
|
@ -44,3 +42,9 @@ and divided into three groups.
|
|||
- [Chain of Responsibility](behavioral/chain_responsibility/README.md)
|
||||
- [Command](behavioral/command/README.md)
|
||||
- [Iterator](behavioral/iterator/README.md)
|
||||
- [Mediator](behavioral/mediator/README.md)
|
||||
|
||||
## Resources
|
||||
|
||||
- [Refactoring Guru](https://refactoring.guru/design-patterns/catalog)
|
||||
- [Python Design Patterns](https://python-patterns.guide/)
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@ Behavioral design patterns are concerned with algorithms and the assignment of r
|
|||
- [Chain of Responsibility](chain_responsibility/README.md)
|
||||
- [Command](command/README.md)
|
||||
- [Iterator](iterator/README.md)
|
||||
- [Mediator](mediator/README.md)
|
||||
|
|
|
|||
39
behavioral/mediator/README.md
Normal file
39
behavioral/mediator/README.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Mediator
|
||||
|
||||
Mediator is a behavioral design pattern that lets you reduce chaotic dependencies between objects. The pattern restricts direct communications between the objects and forces them to collaborate only via a mediator object.
|
||||
|
||||
## Problem
|
||||
|
||||
Say you have a dialog for creating and editing customer profiles. It consists of various form controls such as text fields, checkboxes, buttons, etc.
|
||||
|
||||
Some of the form elements may interact with others. For instance, selecting the “I have a dog” checkbox may reveal a hidden text field for entering the dog’s name. Another example is the submit button that has to validate values of all fields before saving the data.
|
||||
|
||||
By having this logic implemented directly inside the code of the form elements you make these elements’ classes much harder to reuse in other forms of the app. For example, you won’t be able to use that checkbox class inside another form, because it’s coupled to the dog’s text field. You can use either all the classes involved in rendering the profile form, or none at all.
|
||||
|
||||
## Solution
|
||||
|
||||
The Mediator pattern suggests that you should cease all direct communication between the components which you want to make independent of each other. Instead, these components must collaborate indirectly, by calling a special mediator object that redirects the calls to appropriate components. As a result, the components depend only on a single mediator class instead of being coupled to dozens of their colleagues.
|
||||
|
||||
In our example with the profile editing form, the dialog class itself may act as the mediator. Most likely, the dialog class is already aware of all of its sub-elements, so you won’t even need to introduce new dependencies into this class.
|
||||
|
||||
The most significant change happens to the actual form elements. Let’s consider the submit button. Previously, each time a user clicked the button, it had to validate the values of all individual form elements. Now its single job is to notify the dialog about the click. Upon receiving this notification, the dialog itself performs the validations or passes the task to the individual elements. Thus, instead of being tied to a dozen form elements, the button is only dependent on the dialog class.
|
||||
|
||||
You can go further and make the dependency even looser by extracting the common interface for all types of dialogs. The interface would declare the notification method which all form elements can use to notify the dialog about events happening to those elements. Thus, our submit button should now be able to work with any dialog that implements that interface.
|
||||
|
||||
This way, the Mediator pattern lets you encapsulate a complex web of relations between various objects inside a single mediator object. The fewer dependencies a class has, the easier it becomes to modify, extend or reuse that class.
|
||||
|
||||
## How to Implement
|
||||
|
||||
1. Identify a group of tightly coupled classes which would benefit from being more independent (e.g., for easier maintenance or simpler reuse of these classes).
|
||||
|
||||
1. Declare the mediator interface and describe the desired communication protocol between mediators and various components. In most cases, a single method for receiving notifications from components is sufficient.
|
||||
|
||||
This interface is crucial when you want to reuse component classes in different contexts. As long as the component works with its mediator via the generic interface, you can link the component with a different implementation of the mediator.
|
||||
|
||||
1. Implement the concrete mediator class. This class would benefit from storing references to all of the components it manages.
|
||||
|
||||
1. You can go even further and make the mediator responsible for the creation and destruction of component objects. After this, the mediator may resemble a factory or a facade.
|
||||
|
||||
1. Components should store a reference to the mediator object. The connection is usually established in the component’s constructor, where a mediator object is passed as an argument.
|
||||
|
||||
1. Change the components’ code so that they call the mediator’s notification method instead of methods on other components. Extract the code that involves calling other components into the mediator class. Execute this code whenever the mediator receives notifications from that component.
|
||||
5
behavioral/mediator/__init__.py
Normal file
5
behavioral/mediator/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
"""
|
||||
Lets you reduce chaotic dependencies between objects. The pattern restricts
|
||||
direct communications between the objects and forces them to collaborate only
|
||||
via a mediator object.
|
||||
"""
|
||||
19
behavioral/mediator/base_component.py
Normal file
19
behavioral/mediator/base_component.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from behavioral.mediator.mediator import Mediator
|
||||
|
||||
|
||||
class BaseComponent:
|
||||
"""
|
||||
The Base Component provides the basic functionality of storing a mediator's
|
||||
instance inside component objects.
|
||||
"""
|
||||
|
||||
def __init__(self, mediator: Mediator = None) -> None:
|
||||
self._mediator = mediator
|
||||
|
||||
@property
|
||||
def mediator(self) -> Mediator:
|
||||
return self._mediator
|
||||
|
||||
@mediator.setter
|
||||
def mediator(self, mediator: Mediator) -> None:
|
||||
self._mediator = mediator
|
||||
25
behavioral/mediator/components.py
Normal file
25
behavioral/mediator/components.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
"""
|
||||
Concrete Components implement various functionality. They don't depend on other
|
||||
components. They also don't depend on any concrete mediator classes.
|
||||
"""
|
||||
from behavioral.mediator.base_component import BaseComponent
|
||||
|
||||
|
||||
class Component1(BaseComponent):
|
||||
def do_a(self) -> None:
|
||||
print("Component 1 does A.")
|
||||
self.mediator.notify(self, "A")
|
||||
|
||||
def do_b(self) -> None:
|
||||
print("Component 1 does B.")
|
||||
self.mediator.notify(self, "B")
|
||||
|
||||
|
||||
class Component2(BaseComponent):
|
||||
def do_c(self) -> None:
|
||||
print("Component 2 does C.")
|
||||
self.mediator.notify(self, "C")
|
||||
|
||||
def do_d(self) -> None:
|
||||
print("Component 2 does D.")
|
||||
self.mediator.notify(self, "D")
|
||||
20
behavioral/mediator/concrete_mediator.py
Normal file
20
behavioral/mediator/concrete_mediator.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from behavioral.mediator.components import Component2, Component1
|
||||
from behavioral.mediator.mediator import Mediator
|
||||
|
||||
|
||||
class ConcreteMediator(Mediator):
|
||||
def __init__(self, component1: Component1, component2: Component2) -> None:
|
||||
self._component1 = component1
|
||||
self._component1.mediator = self
|
||||
self._component2 = component2
|
||||
self._component2.mediator = self
|
||||
|
||||
def notify(self, sender: object, event: str) -> None:
|
||||
if event == "A":
|
||||
print("Mediator reacts on A and triggers following operations:")
|
||||
self._component2.do_c()
|
||||
|
||||
if event == "D":
|
||||
print("Mediator reacts on D and triggers following operations:")
|
||||
self._component1.do_b()
|
||||
self._component2.do_c()
|
||||
14
behavioral/mediator/main.py
Normal file
14
behavioral/mediator/main.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
"""The client code."""
|
||||
from behavioral.mediator.components import Component1, Component2
|
||||
from behavioral.mediator.concrete_mediator import ConcreteMediator
|
||||
|
||||
c1 = Component1()
|
||||
c2 = Component2()
|
||||
mediator = ConcreteMediator(c1, c2)
|
||||
|
||||
print("Client triggers operation A.")
|
||||
c1.do_a()
|
||||
|
||||
print("")
|
||||
print("Client triggers operation D.")
|
||||
c2.do_d()
|
||||
12
behavioral/mediator/mediator.py
Normal file
12
behavioral/mediator/mediator.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from abc import ABC
|
||||
|
||||
|
||||
class Mediator(ABC):
|
||||
"""
|
||||
The Mediator interface declares a method used by components to notify the
|
||||
mediator about various events. The Mediator may react to these events and
|
||||
pass the execution to other components.
|
||||
"""
|
||||
|
||||
def notify(self, sender: object, event: str) -> None:
|
||||
pass
|
||||
Loading…
Reference in a new issue