Observer Kalıbı
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 94 / 104.
Observer deseni, nesneler arasında bire-çok bir bağımlılık tanımlar: bir nesne (subject) durum değiştirdiğinde, tüm bağımlıları (observers) otomatik olarak bilgilendirilir. Bu; olay sistemleri, kullanıcı arayüzü (UI) güncellemeleri veya birden fazla nesnenin değişikliklere tepki vermesi gereken her türlü senaryo için idealdir.
Bu desen iki temel rol içerir: gözlemcilerin bir listesini tutan ve onları bilgilendiren bir Subject ve bir güncelleme arayüzü uygulayan Observers:
#include <iostream>
#include <vector>
#include <algorithm>
class Observer {
public:
virtual void update(int value) = 0;
virtual ~Observer() = default;
};
class Subject {
std::vector<Observer*> observers;
int state = 0;
public:
void attach(Observer* obs) {
observers.push_back(obs);
}
void detach(Observer* obs) {
observers.erase(
std::remove(observers.begin(), observers.end(), obs),
observers.end());
}
void setState(int value) {
state = value;
notify();
}
void notify() {
for (Observer* obs : observers) {
obs->update(state);
}
}
};
class Display : public Observer {
std::string name;
public:
Display(const std::string& n) : name(n) {}
void update(int value) override {
std::cout << name << " received: " << value << "\n";
}
};
int main() {
Subject sensor;
Display screen1("Screen1"), screen2("Screen2");
sensor.attach(&screen1);
sensor.attach(&screen2);
sensor.setState(42); // Her iki ekran da bilgilendirildi
}setState() çağrıldığında, konu (subject) kayıtlı tüm gözlemciler üzerinde döner ve onların update() metodunu çağırır. Gözlemciler herhangi bir zamanda eklenebilir veya ayrılabilir, bu da sistemi esnek ve gevşek bağlı hale getirir.
Bir nesnedeki değişiklikler diğerlerinin güncellenmesini gerektirdiğinde ve bu nesnelerin birbirine sıkı sıkıya bağlı olmasını istemediğinizde Observer desenini kullanın.
Görev
KolayObserver desenini kullanarak bir Hava Durumu İstasyonu (Weather Station) izleme sistemi oluşturalım. Sıcaklık verilerini takip eden ve sıcaklık her değiştiğinde birden fazla ekran ünitesini otomatik olarak bilgilendiren bir hava durumu istasyonu oluşturacaksınız; bu, bu desenin gerçek dünyadaki klasik bir uygulamasıdır.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
Observer.h: Gözlemci (observer) arayüzünü ve somut bir ekran gözlemcisini tanımlayın.Gözlemcilerin bildirimleri almak için uyguladığı saf sanal bir
update(double temperature)metoduna ve sanal bir yıkıcıya (destructor) sahip soyut birObserversınıfı oluşturun.Ardından
Observersınıfından miras alan birTemperatureDisplaysınıfı oluşturun. Her ekranın yapıcı (constructor) aracılığıyla ayarlanan bir adı (string) vardır.update()çağrıldığında şunu yazdırmalıdır:[DisplayName]: Temperature is [temperature] degreesWeatherStation.h: Sıcaklığı tutan ve gözlemcileri bilgilendiren konuyu (subject) oluşturun.WeatherStationsınıfınız şunları yapmalıdır:- Gözlemci işaretçilerinin (pointers) bir listesini ve mevcut sıcaklığı saklamalıdır (başlangıç değeri
0.0) - Gözlemcileri kaydetmek için bir
attach(Observer* obs)metoduna sahip olmalıdır - Gözlemcileri kaldırmak için bir
detach(Observer* obs)metoduna sahip olmalıdır - Sıcaklığı güncelleyen ve tüm bağlı gözlemcileri bilgilendiren bir
setTemperature(double temp)metoduna sahip olmalıdır - Her gözlemci üzerinde mevcut sıcaklık ile
update()metodunu çağıran özel (private) birnotify()metoduna sahip olmalıdır
- Gözlemci işaretçilerinin (pointers) bir listesini ve mevcut sıcaklığı saklamalıdır (başlangıç değeri
main.cpp: Observer desenini iş başında gösterin.Üç girdi okuyun:
- İlk ekran için isim (string)
- İkinci ekran için isim (string)
- Bir sıcaklık değeri (double)
Bir
WeatherStationve sağlanan isimlerle ikiTemperatureDisplaynesnesi oluşturun. Her iki ekranı da istasyona bağlayın (attach), ardından sıcaklığı girdi değerine ayarlayın. Her iki ekran da güncellemeyi otomatik olarak almalı ve mesajlarını yazdırmalıdır.Bundan sonra, ilk ekranı ayırın (detach) ve girdiden 5 derece daha yüksek yeni bir sıcaklık ayarlayın. Bu güncellemeyi yalnızca ikinci ekran almalıdır.
Örneğin, Kitchen, Bedroom ve 22.5 girdileriyle:
Kitchen: Temperature is 22.5 degrees
Bedroom: Temperature is 22.5 degrees
Bedroom: Temperature is 27.5 degreesOffice, Lobby ve 18.0 girdileriyle:
Office: Temperature is 18 degrees
Lobby: Temperature is 18 degrees
Lobby: Temperature is 23 degreesObserver deseninin nasıl gevşek bağlı (loosely coupled) bir sistem oluşturduğuna dikkat edin; hava durumu istasyonunun belirli ekranlar hakkında bir şey bilmesine gerek yoktur, sadece dinleyen herkesi bilgilendirir. Bir gözlemciyi ayırdığınızda, güncellemeleri almayı otomatik olarak durdurur.
Kopya kağıdı
Observer deseni, bir konunun (subject) durumu değiştiğinde tüm gözlemcilerini (observers) otomatik olarak bilgilendirdiği bire-çok bir bağımlılık tanımlar.
Desen iki temel role sahiptir:
- Subject (Konu): Gözlemcilerin bir listesini tutar ve durum değişikliklerini onlara bildirir
- Observer (Gözlemci): Bildirimleri almak için bir güncelleme arayüzü uygular
Temel uygulama:
#include <iostream>
#include <vector>
#include <algorithm>
class Observer {
public:
virtual void update(int value) = 0;
virtual ~Observer() = default;
};
class Subject {
std::vector<Observer*> observers;
int state = 0;
public:
void attach(Observer* obs) {
observers.push_back(obs);
}
void detach(Observer* obs) {
observers.erase(
std::remove(observers.begin(), observers.end(), obs),
observers.end());
}
void setState(int value) {
state = value;
notify();
}
void notify() {
for (Observer* obs : observers) {
obs->update(state);
}
}
};
class Display : public Observer {
std::string name;
public:
Display(const std::string& n) : name(n) {}
void update(int value) override {
std::cout << name << " received: " << value << "\n";
}
};Temel metotlar:
attach(): Bildirimleri almak için bir gözlemci kaydederdetach(): Bir gözlemciyi bildirim listesinden çıkarırnotify(): Tüm gözlemciler üzerinde döner ve onlarınupdate()metodunu çağırır
Bir nesnedeki değişikliklerin, aralarında sıkı bir bağ (tight coupling) olmadan diğerlerini güncellemeyi gerektirdiği durumlarda Observer desenini kullanın.
Kendin dene
#include <iostream>
#include <string>
#include "WeatherStation.h"
using namespace std;
int main() {
// Girdileri oku
string display1Name, display2Name;
double temperature;
cin >> display1Name;
cin >> display2Name;
cin >> temperature;
// TODO: Bir WeatherStation nesnesi oluşturun
// TODO: Giriş adlarıyla iki TemperatureDisplay nesnesi oluşturun
// TODO: Her iki ekranı da hava durumu istasyonuna bağlayın
// TODO: Sıcaklığı giriş değerine ayarlayın
// (Her iki ekran da mesajlarını yazdırmalıdır)
// TODO: İlk ekranın bağlantısını kesin
// TODO: Sıcaklığı girişten 5 derece daha yükseğe ayarlayın
// (Sadece ikinci ekran yazdırmalıdır)
return 0;
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner