feat(cmat): implement Ex41 observer pattern

Refactor the Pelican crossing exercise to use the Observer
pattern: Pelican acts as Subject and each Pole observes it,
replacing direct calls through the owned Pole pointer.

Co-authored-by: Qwen 3.8 27B <qwen3.8_27b-q3_k_xl@lmstudio.local>
This commit is contained in:
2026-10-06 16:26:53 +02:00
co-authored by Qwen 3.8 27B
parent c681ebab2b
commit 6e0e45110a
8 changed files with 39 additions and 28 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
class Observer { class Observer {
public: public:
// TODO: declare the pure virtual function update() virtual void update() = 0;
}; };
#endif #endif
+6 -11
View File
@@ -3,29 +3,25 @@
#include "PelicanTimer.h" #include "PelicanTimer.h"
#include "Pole.h" #include "Pole.h"
Pelican::Pelican(int x, int y) Pelican::Pelican()
: pressed(false), RED_TIME(4), YELLOW_TIME(2), GREEN_TIME(3), FLASH_TIME(5), myPole(new Pole(x, y)), : pressed(false), RED_TIME(4), YELLOW_TIME(2), GREEN_TIME(3), FLASH_TIME(5),
state(GREEN_IDLE) { state(GREEN_IDLE) {
myTimer = new PelicanTimer(*this, &Pelican::timerExpired); myTimer = new PelicanTimer(*this, &Pelican::timerExpired);
myPole->setTrafficLights(getState()); notify();
myPole->setWaitLight(isPressed());
} }
Pelican::~Pelican() { delete myPole; } Pelican::~Pelican() { }
void Pelican::press(void) { void Pelican::press(void) {
if (state == GREEN_IDLE) { if (state == GREEN_IDLE) {
state = YELLOW_STATE; state = YELLOW_STATE;
myTimer->timeoutRequest(YELLOW_TIME); myTimer->timeoutRequest(YELLOW_TIME);
pressed = true; pressed = true;
myPole->setTrafficLights(getState());
myPole->setWaitLight(isPressed());
} else if (state != RED_STATE) { } else if (state != RED_STATE) {
pressed = true; pressed = true;
myPole->setTrafficLights(getState());
myPole->setWaitLight(isPressed());
} }
notify();
} }
void Pelican::timerExpired(void) { void Pelican::timerExpired(void) {
@@ -60,6 +56,5 @@ void Pelican::timerExpired(void) {
break; break;
} }
myPole->setTrafficLights(getState()); notify();
myPole->setWaitLight(isPressed());
} }
+4 -4
View File
@@ -7,12 +7,14 @@
#ifndef PELICAN_H #ifndef PELICAN_H
#define PELICAN_H #define PELICAN_H
#include "Subject.h"
class PelicanTimer; class PelicanTimer;
class Pole; class Pole;
class Pelican { class Pelican : public Subject {
public: public:
Pelican(int x, int y); Pelican();
~Pelican(); ~Pelican();
// call when the WAIT button has been pressed // call when the WAIT button has been pressed
@@ -39,8 +41,6 @@ class Pelican {
// indicates whether the WAIT light should be on // indicates whether the WAIT light should be on
bool pressed; bool pressed;
Pole *myPole;
// Timer that indicates when the lights should change // Timer that indicates when the lights should change
PelicanTimer *myTimer; PelicanTimer *myTimer;
+7 -1
View File
@@ -1,8 +1,9 @@
#include "Pole.h" #include "Pole.h"
#include "Screen.h" #include "Screen.h"
Pole::Pole(int x, int y) : waitLight(new Light("WAIT", x + 6, y + 12, Screen::WHITE)) { Pole::Pole(int x, int y, Pelican& pelican) : waitLight(new Light("WAIT", x + 6, y + 12, Screen::WHITE)), pelican(pelican) {
using namespace Screen; // we will discuss namespaces in the course using namespace Screen; // we will discuss namespaces in the course
pelican.attach(this);
lights[RED_LIGHT] = new Light("*", x + 11, y + 3, RED); lights[RED_LIGHT] = new Light("*", x + 11, y + 3, RED);
lights[RED_MAN] = new Light("*<-=", x + 6, y + 4, RED); lights[RED_MAN] = new Light("*<-=", x + 6, y + 4, RED);
@@ -57,3 +58,8 @@ void Pole::setTrafficLights(Pelican::PelicanState state) {
} }
void Pole::setWaitLight(bool pressed) { waitLight->set(pressed ? Light::ON : Light::OFF); } void Pole::setWaitLight(bool pressed) { waitLight->set(pressed ? Light::ON : Light::OFF); }
void Pole::update() {
setTrafficLights(pelican.getState());
setWaitLight(pelican.isPressed());
}
+6 -3
View File
@@ -3,22 +3,25 @@
#define POLE_H #define POLE_H
#include "Light.h" #include "Light.h"
#include "Observer.h"
#include "Pelican.h" #include "Pelican.h"
class Pole { class Pole : public Observer {
public: public:
Pole(int x, int y); Pole(int x, int y, Pelican&);
~Pole(); ~Pole();
void setTrafficLights(Pelican::PelicanState state); void setTrafficLights(Pelican::PelicanState state);
void setWaitLight(bool pressed); void setWaitLight(bool pressed);
void update();
private: private:
enum LightName { RED_LIGHT, YELLOW_LIGHT, GREEN_LIGHT, GREEN_MAN, RED_MAN }; enum LightName { RED_LIGHT, YELLOW_LIGHT, GREEN_LIGHT, GREEN_MAN, RED_MAN };
Light *lights[RED_MAN + 1]; Light *lights[RED_MAN + 1];
Light *waitLight; Light *waitLight;
Pelican& pelican;
}; };
#endif #endif
+8 -4
View File
@@ -1,8 +1,12 @@
#include "Subject.h" #include "Subject.h"
#include "Observer.h" #include "Observer.h"
// TODO: Implement the Subject::attach() member function that adds an Observer to the set void Subject::attach(Observer* observer) {
m_observers.insert(observer);
}
// TODO: Implement the Subject::notify() member function that calls all the update functions void Subject::notify() {
for (auto* observer : m_observers) {
observer->update();
}
}
+3 -3
View File
@@ -8,11 +8,11 @@ class Observer;
class Subject { class Subject {
public: public:
// TODO: Declare the attach() function that adds an Observer to the set void attach(Observer* observer);
protected: protected:
// TODO: Declare the notify() function that calls all the update functions void notify();
private: private:
// TODO: Define a set of Observer pointers std::set<Observer*> m_observers;
}; };
#endif #endif
+4 -1
View File
@@ -1,13 +1,16 @@
#include "Keyboard.h" #include "Keyboard.h"
#include "Pelican.h" #include "Pelican.h"
#include "Pole.h"
#include "TimerSet.h" #include "TimerSet.h"
#include <cctype> // provides toupper() #include <cctype> // provides toupper()
int main() { int main() {
Keyboard kbd; Keyboard kbd;
Pelican thePelican(30, 0); Pelican thePelican;
Pole pole1(20, 0, thePelican);
Pole pole2(40, 0, thePelican);
char c = 0; char c = 0;