#include <iostream>
#include <set>
class Observer
{
public:
virtual void update() = 0;
};
class Subject
{
std::set<Observer*> observers;
public:
void addObserver(Observer* observer)
{
observers.insert(observer);
}
void deleteObserver(Observer* observer)
{
observers.erase(observer);
}
void notifyObservers()
{
for (auto observer : observers)
observer->update();
}
};
class HaveValue1 { public: virtual int getValue1() const = 0; };
class HaveValue2 { public: virtual int getValue2() const = 0; };
class ObservingForValue1 : public Observer
{
HaveValue1* subject;
public:
ObservingForValue1(HaveValue1* subject) : subject(subject) {}
virtual void update()
{
std::cout << "New value of Value1: " << subject->getValue1() << '\n';
}
};
class ObservingForValue2 : public Observer
{
HaveValue2* subject;
public:
ObservingForValue2(HaveValue2* subject) : subject(subject) {}
virtual void update()
{
std::cout << "New value of Value2: " << subject->getValue2() << '\n';
}
};
class UsingInheritance : public HaveValue1, public HaveValue2, public Subject
{
int value1;
int value2;
public:
UsingInheritance(int value1, int value2) : value1(value1), value2(value2) {}
void setValue1(int value) { value1 = value; notifyObservers(); }
void setValue2(int value) { value2 = value; notifyObservers(); }
virtual int getValue1() const override { return value1; }
virtual int getValue2() const override { return value2; }
};
class UsingComposition : public HaveValue1, public HaveValue2
{
int value1;
int value2;
Subject observersForValue1;
Subject observersForValue2;
public:
UsingComposition(int value1, int value2) : value1(value1), value2(value2) {}
void observeValue1(Observer* observer) { observersForValue1.addObserver(observer); }
void observeValue2(Observer* observer) { observersForValue2.addObserver(observer); }
void unobserveValue1(Observer* observer) { observersForValue1.deleteObserver(observer); }
void unobserveValue2(Observer* observer) { observersForValue2.deleteObserver(observer); }
void setValue1(int value)
{ value1 = value; observersForValue1.notifyObservers(); }
void setValue2(int value)
{ value2 = value; observersForValue2.notifyObservers(); }
virtual int getValue1() const override { return value1; }
virtual int getValue2() const override { return value2; }
};
int main()
{
std::cout << "With Inheritance:\n";
UsingInheritance inheritance{ 1, 2 };
ObservingForValue1 io1{ &inheritance };
ObservingForValue2 io2{ &inheritance };
inheritance.addObserver(&io1);
inheritance.addObserver(&io2);
inheritance.setValue1(11);
inheritance.setValue2(22);
std::cout << "\nWith Composition:\n";
UsingComposition composition{ 1, 2 };
ObservingForValue1 co1{ &composition };
ObservingForValue2 co2{ &composition };
composition.observeValue1(&co1);
composition.observeValue2(&co2);
composition.setValue1(11);
composition.setValue2(22);
}
Вывод:
With Inheritance:
New value of Value2: 2
New value of Value1: 11
New value of Value2: 22
New value of Value1: 11
With Composition:
New value of Value1: 11
New value of Value2: 22
Я узнал шаблон observer
из многих источников, но ни один из них не упомянул о его реализации с помощью композиции. Я думаю, что использование композиции является более мощным, чем наследование, по нескольким причинам:
1 - класс может иметь много наблюдателей, и каждый из них наблюдает за чем-то другим. Если бы я хотел добиться того же поведения (имея разных наблюдателей, наблюдающих за n
разными вещами), мне пришлось бы изменить базовый класс так, чтобы он поддерживал n
типов наблюдателей и позже, если бы я хотел, чтобы он поддерживал n + 1
наблюдатели, мне придется изменить это снова или унаследовать от другого класса. В то время как при использовании композиции я могу просто добавить еще один Subject
и все.
2 - С наследованием, если я захочу уведомить наблюдателей, я уведомлю всех подписавшихся, даже если каждый из них ищет что-то другое. Принимая во внимание, что при использовании композиции я могу просто уведомить желаемые.
3 - Некоторые языки, такие как java
, не допускают множественное наследование, если я наследую от абстрактного класса, который реализует некоторые логики c из Subject
, я не смогу наследовать от другого учебный класс. В то время как при использовании композиции у некоторых классов есть свободное место для наследования.
4 - Композиция в любом случае предпочтительнее наследования. Если какое-либо поведение достижимо с использованием как наследования, так и композиции, почему подход наследования более популярен?
В приведенном выше коде у меня есть Observer
и Subject
. Существует 2 типа Subject
, один из которых использует наследование с именем UsingInheritance
, а другой использует композицию с именем UsingComposition
. Каждое из них имеет 2 значения, value1
и value2
. Есть 2 наблюдателя, ObservingForValue1
, которые заинтересованы только в value1
и ObservingForValue2
, которые заинтересованы только в value2
. Обратите внимание, что при использовании UsingInheritance
все подписанные наблюдатели получают уведомление, даже если значение, в котором они заинтересованы, не изменяется, тогда как при использовании UsingComposition
обновляются только нужные.