trainings/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer3.cpp

101 lines
No EOL
2.6 KiB
C++

#include <algorithm>
#include <cassert>
#include <functional>
#include <iostream>
#include <memory>
#include <set>
#include <vector>
template <typename Subject, typename State>
struct Observer {
// First, we don't need to use polymorphism, so also virtual D'tor is redundant
using OnUpdate = std::function<void(const Subject&, State)>;
explicit Observer(OnUpdate fun)
: _onUpdate(std::move(fun)) {}
void update(const Subject& subject, State state) {
std::invoke(_onUpdate, subject, state);
}
private:
OnUpdate _onUpdate;
};
class Browser;
enum class BrowserState {
Closing,
FocusChanged
};
using BrowserObserver = Observer<Browser, BrowserState>;
class Browser {
public:
void attach(BrowserObserver* observer) { _observers.emplace(observer); }
void detach(BrowserObserver* observer) { _observers.erase(observer); }
void setFocus(bool focus) {
if (std::exchange(_hasFocus, focus) != focus) {
notify(BrowserState::FocusChanged);
}
}
bool hasFocus() const { return _hasFocus; }
private:
void notify(BrowserState state) const {
std::ranges::for_each(_observers, [state, this](auto* observer) {
observer->update(*this, state);
});
}
bool _hasFocus{false};
std::set<BrowserObserver*> _observers;
};
class Extension {
public:
explicit Extension(const std::string& name)
: _name(name) {}
// D'tor is not needed neither the rule of 5
// dispatch message
void update(const Browser& browser, BrowserState state) {
switch (state) {
case BrowserState::Closing:
return;
case BrowserState::FocusChanged: {
if (browser.hasFocus()) {
displayExtension();
}
return;
}
}
}
private:
void displayExtension() {
std::cout << "This is an extension: " << _name << '\n';
}
std::string _name;
};
int main() {
std::unique_ptr<Browser> browser = std::make_unique<Browser>();
Extension extension1("A");
Extension extension2("B");
Extension extension3("C");
BrowserObserver observer1([&extension1](const Browser& browser, BrowserState state) {
extension1.update(browser, state);
});
BrowserObserver observer2([&extension2](const Browser& browser, BrowserState state) {
extension2.update(browser, state);
});
BrowserObserver observer3([&extension3](const Browser& browser, BrowserState state) {
extension3.update(browser, state);
});
browser->attach(&observer1);
browser->attach(&observer2);
browser->attach(&observer3);
browser->setFocus(true);
}