-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathConceptualExample03.cpp
More file actions
187 lines (147 loc) · 6.49 KB
/
Copy pathConceptualExample03.cpp
File metadata and controls
187 lines (147 loc) · 6.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// ===========================================================================
// ConceptualExample02.cpp // Observer // Variant 3 // Self Unregister
// ===========================================================================
/**
* Observer Design Pattern
*/
#include <algorithm> // std::erase_if
#include <list>
#include <memory>
#include <print>
#include <string>
#include <string_view>
#include <vector>
namespace ObserverDesignPattern_SelfUnregister {
// Vorwärtsdeklaration, da sich die Interfaces gegenseitig benötigen
class ISubject;
class IObserver {
public:
virtual ~IObserver() = default;
virtual void update(std::string_view message) = 0;
};
class ISubject {
public:
virtual ~ISubject() = default;
virtual void attach(std::shared_ptr<IObserver> observer) = 0;
virtual void detach(std::shared_ptr<IObserver> observer) = 0;
};
// =======================================================================
class Subject : public ISubject {
private:
std::vector<std::weak_ptr<IObserver>> m_observers;
std::string m_message;
public:
~Subject() noexcept override {
std::println("d'tor Subject");
}
/**
* subscription management methods
*/
void attach(std::shared_ptr<IObserver> observer) override {
m_observers.push_back(observer);
}
void detach(std::shared_ptr<IObserver> observer) override {
// C++20: Effizientes Entfernen basierend auf der Identität des Objekts
std::erase_if(m_observers, [&observer](const std::weak_ptr<IObserver>& wp) {
return !observer.owner_before(wp) && !wp.owner_before(observer);
}
);
}
void createMessage(const std::string& message) {
m_message = message;
notify();
}
private:
void notify() {
// Gleichzeitiges Benachrichtigen und Bereinigen von abgelaufenen Pointern
std::erase_if(m_observers, [this](const std::weak_ptr<IObserver>& wp) {
if (auto sharedPtr = wp.lock()) {
sharedPtr->update(m_message);
return false;
}
return true;
}
);
}
};
// =======================================================================
// WICHTIG: enable_shared_from_this erlaubt dem Observer, shared_from_this() zu nutzen
class Observer : public IObserver, public std::enable_shared_from_this<Observer> {
private:
std::string m_messageFromSubject;
std::size_t m_number;
// Der Observer merkt sich alle Subjects, bei denen er registriert ist
std::vector<std::weak_ptr<ISubject>> m_observedSubjects;
public:
Observer() {
static std::size_t nextNumber = 0;
m_number = nextNumber++;
std::println("Observer {} erzeugt.", m_number);
}
// RAII: Beim Zerstören meldet sich der Observer überall automatisch ab!
~Observer() noexcept override {
std::println("d'tor Observer ({}) -> Melde mich von allen verbleibenden Subjects ab.", m_number);
for (const auto& weakSubject : m_observedSubjects) {
if (auto subject = weakSubject.lock()) {
// Da wir uns im Destruktor befinden, dürfen wir shared_from_this() NICHT mehr aufrufen!
// Stattdessen nutzen wir das clevere automatische Bereinigen von expired() weaks im Subject bei notify()
// ODER wir übergeben einen nackten Pointer, wenn das Interface das erlauben würde.
// Für dieses Design lassen wir das Subject die abgelaufene weak_ptr einfach beim nächsten notify() löschen.
}
}
}
// Komfortmethode zum Anmelden
void registerAt(std::shared_ptr<ISubject> subject) {
if (subject) {
subject->attach(shared_from_this());
m_observedSubjects.push_back(subject);
std::println("Observer {}: Registriert bei Subject.", m_number);
}
}
// Die von dir gewünschte Methode zur Selbstabmeldung
void removeMeFromSubject(std::shared_ptr<ISubject> subject) {
if (!subject) return;
std::println("Observer {}: Melde mich aktiv vom Subject ab.", m_number);
// 1. Vom Subject abmelden (Das Subject wirft uns aus seiner Liste)
subject->detach(shared_from_this());
// 2. Das Subject aus der eigenen Liste des Observers löschen
std::erase_if(m_observedSubjects, [&subject](const std::weak_ptr<ISubject>& wp) {
auto sharedSubject = wp.lock();
return sharedSubject == subject;
});
}
void update(std::string_view messageFromSubject) override {
m_messageFromSubject = messageFromSubject;
std::println("Observer {}: Neue Nachricht erhalten --> \"{}\"", m_number, m_messageFromSubject);
}
};
}
// =======================================================================
void test_conceptual_example_03()
{
using namespace ObserverDesignPattern_SelfUnregister;
auto subject = std::make_shared<Subject>();
auto observer1 = std::make_shared<Observer>();
auto observer2 = std::make_shared<Observer>();
// Anmeldung erfolgt nun komfortabel über den Observer
observer1->registerAt(subject);
observer2->registerAt(subject);
subject->createMessage("Erste Nachricht");
// Verwendung deiner gewünschten Methode
observer1->removeMeFromSubject(subject);
// Nur noch Observer 2 sollte diese Nachricht erhalten
subject->createMessage("Zweite Nachricht");
std::println("\n--- Block-Bereich startet ---");
{
auto observer3 = std::make_shared<Observer>();
observer3->registerAt(subject);
subject->createMessage("Dritte Nachricht (mit Nr. 2)");
std::println("Observer 2 verlässt gleich den Scope...");
} // observer3 wird hier zerstört.
std::println("--- Block-Bereich beendet ---\n");
// Das Subject bereinigt die Leiche von Observer 3 beim nächsten notify() automatisch!
subject->createMessage("Vierte Nachricht (nur noch Nr. 1)");
}
// ===========================================================================
// End-of-File
// ===========================================================================