diff --git a/CMakeLists.txt b/CMakeLists.txt index eb4f7b3..24e1d05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,7 @@ set(APP_SOURCES "src/patterns/behavioral/ChainOfCommand.cpp" "src/patterns/behavioral/Command.cpp" "src/patterns/behavioral/Iterator.cpp" + "src/patterns/behavioral/Mediator.cpp" ) # Test files diff --git a/docs/uml/patterns_behavioral_iterator.drawio.svg b/docs/uml/patterns_behavioral_iterator.drawio.svg index ffa0cf9..afbba43 100644 --- a/docs/uml/patterns_behavioral_iterator.drawio.svg +++ b/docs/uml/patterns_behavioral_iterator.drawio.svg @@ -1,4 +1,4 @@ -
Client
+ clientCode(IAggregate<T> * collection): type

<<Interface>>
IAggregate


+ createIterator: IIterator*

<<Interface>>
IIterator


+ hasNext(): bool

+ next(): *T

VectorConcreteIterator
m_container: const std::vector<T>* const
- size_t m_currentIndex{0};

+ hasNext(): bool

+ next(): *T

ListConcreteIterator
+ m_org_container:const std::vector<T>* const
- size_t m_currentIndex{0};

+ hasNext(): bool

+ next(): *T

VectorConcreteAggregate
- m_container: std::vector<T> 
+ add(T i): void
+ createIterator: IIterator*
ListConcreteAggregate
- m_container: std::list<T> 
+ add(T i): void
+ createIterator: IIterator*
Use
Use
<<create>>
<<create>>
<<create>>
1
1
Extends
Extends
Extends
Extends
createIterator:
{
return new VectorConcreteIterator<T>(m_data);
}
hasNext:
{
return m_currentIndex < m_container.size();
}

next:
{
  if (hasNext()) { 
return (T *)&m_data[m_currentIndex++]; 
  } else { 
return nullptr; 
  }
}
clientCode:
{
IIterator<T> *iterator = collection->createIterator();
if (iterator != nullptr) {
  while (iterator->hasNext()) 
const T* child = iterator->next(); 

  }
delete iterator;
}
\ No newline at end of file +
Client
+ clientCode(IAggregate<T> * collection): type

<<Interface>>
IAggregate


+ createIterator: IIterator*

<<Interface>>
IIterator


+ hasNext(): bool

+ next(): *T

VectorConcreteIterator
m_container: const std::vector<T>* const
- size_t m_currentIndex{0};

+ hasNext(): bool

+ next(): *T

ListConcreteIterator
+ m_org_container:const std::vector<T>* const
- size_t m_currentIndex{0};

+ hasNext(): bool

+ next(): *T

VectorConcreteAggregate
- m_container: std::vector<T> 
+ add(T i): void
+ createIterator: IIterator*
ListConcreteAggregate
- m_container: std::list<T> 
+ add(T i): void
+ createIterator: IIterator*
Use
Use
<<create>>
<<create>>
<<create>>
1
1
Implement
Extends
Implement
Extends
createIterator:
{
return new VectorConcreteIterator<T>(m_data);
}
hasNext:
{
return m_currentIndex < m_container.size();
}

next:
{
  if (hasNext()) { 
return (T *)&m_data[m_currentIndex++]; 
  } else { 
return nullptr; 
  }
}
clientCode:
{
IIterator<T> *iterator = collection->createIterator();
if (iterator != nullptr) {
  while (iterator->hasNext()) 
const T* child = iterator->next(); 

  }
delete iterator;
}
\ No newline at end of file diff --git a/docs/uml/patterns_behavioral_mediator.drawio.svg b/docs/uml/patterns_behavioral_mediator.drawio.svg new file mode 100644 index 0000000..568e7af --- /dev/null +++ b/docs/uml/patterns_behavioral_mediator.drawio.svg @@ -0,0 +1,4 @@ + + + +

<<Interface>>
IComponent


+ send(Event e): type

+ receive(Event e): type




<<Abstract>>
BaseComponent


# m_mediator: IMediator*



<<Interface>>
IMediator


+ registerComponent(IComponent* const comp): void

+ notify(IComponent* sender, Event event): type

Event
+ CREATE
+ READ
+ UPDATE
+ DELETE
ConcreteComponent

+ send(Event e): type

+ receive(Event e): type

ConcreteMediator
- m_components: Collection<IComponent*>

+ registerComponent(IComponent* const comp): void

+ notify(IComponent* sender, Event event): type

main:
{
IMediator* mediator = new ConcreteMediator();
IComponent* comp_1= new  ConcreteComponent(mediator);
IComponent* comp_2= new  ConcreteComponent(mediator);
IComponent* comp_3= new  ConcreteComponent(mediator);
IComponent* comp_4= new  ConcreteComponent(mediator);

// Only c1, c3, c3 receive notifications.
mediator->registerComponent(comp_1);
mediator->registerComponent(comp_2);
mediator->registerComponent(comp_3);

clientCode(comp_4);
}

Client:
{
 comp->send(Event::UPDATE);
}
Client
+ clientCode(IComponent* comp): type
Use
Extends
0..n
1
send:
{
 if(m_mediator != nullptr){
m_mediator->notify(this,e);
}
}


receive:
{
// Additional handling logic can go here
}
register:
{
 m_components.add(comp);
}


notify:
{
 for(auto c : m_components){
if(c!= sender){
c->receive(e);
}
}
}
\ No newline at end of file diff --git a/src/patterns/behavioral/Mediator.cpp b/src/patterns/behavioral/Mediator.cpp new file mode 100644 index 0000000..f262e13 --- /dev/null +++ b/src/patterns/behavioral/Mediator.cpp @@ -0,0 +1,179 @@ +// 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. +// Usage examples: The most popular usage of the Mediator pattern in C++ code is facilitating communications between GUI components of an app. +// The synonym of the Mediator is the `Controller` part of MVC pattern. +// Appicability: +// (*) when your collection has a complex data structure under the hood, but you want to hide its complexity from clients (either for convenience or security reasons). +// (**) when you can’t reuse a component in a different program because it’s too dependent on other components. +// (***) when you find yourself creating tons of component subclasses just to reuse some basic behavior in various contexts. + +// UML: docs/uml/patterns_behavioral_mediator.drawio.svg + +#include +#include +#include +namespace +{ + namespace Mediator + { + + enum class Event + { + CREATE = 0, + READ, + UPDATE, + DELETE, + }; + + static inline const char *getEventName(const Event &e) + { + switch (e) + { + case Event::CREATE: + return "CREATE"; + case Event::READ: + return "READ"; + case Event::UPDATE: + return "UPDATE"; + case Event::DELETE: + return "DELETE"; + } + return "invalid_event"; + } + + class IComponent + { + public: + virtual ~IComponent() = default; + + virtual void send(const Event e) = 0; + virtual void receive(const Event e) = 0; + }; + + /** + * The Mediator interface declares methods of communication with components, which usually include just a single notification method. + * Components may pass any context as arguments of this method, including their own objects, + * but only in such a way that no coupling occurs between a receiving component and the sender’s class. + */ + class IMediator + { + public: + virtual ~IMediator() = default; + virtual void registerComponent(IComponent *const c) = 0; + virtual void notify(IComponent *sender, const Event &e) = 0; + }; + + /** + * Concrete Mediators implement cooperative behavior by coordinating several components. + * Concrete mediators often keep references to all components they manage and sometimes even manage their lifecycle. + */ + class ComponentMediator : public IMediator + { + private: + std::vector m_components; + + public: + void registerComponent(IComponent *const c) override + { + m_components.push_back(c); + } + + void notify(IComponent *const sender, const Event &e) override + { + for (auto c : m_components) + { + if (c != sender) + { + c->receive(e); + } + } + } + }; + + /** + * Components are various classes that contain some business logic. + * Each component has a reference to a mediator, declared with the type of the mediator interface. + * The component isn’t aware of the actual class of the mediator, so you can reuse the component in other programs by linking it to a different mediator. + */ + class AbstractComponent : public IComponent + { + private: + const std::string m_id; + + protected: + IMediator *m_mediator; + void log(const Event &e, const std::string &msg) const + { + std::cout << "\t" + msg + "-id:" + m_id + "-event:" + getEventName(e) + "\n"; + } + + public: + explicit AbstractComponent(const std::string &id, IMediator *const m = nullptr) : m_id{id}, m_mediator{m} {}; + }; + + /** + * Concrete Components implement various functionality. They don't depend on + * other components. They also don't depend on any concrete mediator classes. + */ + class ConreteComponent : public AbstractComponent + { + public: + explicit ConreteComponent(const std::string &id, IMediator *const m = nullptr) : AbstractComponent{id, m} {} + + void send(const Event e) override + { + log(e, "[SEND]"); + if (m_mediator != nullptr) + m_mediator->notify(this, e); + } + + void receive(const Event e) override + { + log(e, "[RECV]"); + // Additional handling logic can go here + } + }; + + namespace Client + { + void clientCode(IComponent *comp) + { + comp->send(Event::READ); + } + } + + void run() + { + IMediator *mediator = new ComponentMediator(); + IComponent *c1 = new ConreteComponent("1763700876", mediator); + IComponent *c2 = new ConreteComponent("1763700882", mediator); + IComponent *c3 = new ConreteComponent("1763700899", mediator); + IComponent *c4 = new ConreteComponent("1763700900", mediator); + + // Only c1, c3, c4 receive notifications. + mediator->registerComponent(c1); + mediator->registerComponent(c3); + mediator->registerComponent(c4); + + // c2 triggers event => observed by others + Client::clientCode(c2); + + delete mediator; + delete c1; + delete c2; + delete c3; + delete c4; + } + } +} + +struct MediatorAutoRunner +{ + MediatorAutoRunner() + { + std::cout << "\n--- Mediator Pattern Example ---\n"; + Mediator::run(); + } +}; + +static MediatorAutoRunner instance; \ No newline at end of file