Observer Kalıbı
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 89 / 107.
Observer deseni, nesneler arasında bire-çok ilişkisini tanımlar. Bir nesne (konu) durumunu değiştirdiğinde, ona bağlı tüm nesneler (gözlemciler) otomatik olarak bilgilendirilir. Bu, olay sistemleri, bildirimler veya birden fazla bileşenin değişikliklere tepki vermesi gereken her senaryo için idealdir.
Go'da bu modeli, gözlemciler için bir arayüz ve abonelikleri yöneten bir subject yapısı kullanarak uygularız:
type Observer interface {
Update(event string)
}
type Subject struct {
observers []Observer
}
func (s *Subject) Subscribe(o Observer) {
s.observers = append(s.observers, o)
}
func (s *Subject) Notify(event string) {
for _, observer := range s.observers {
observer.Update(event)
}
}Observer arayüzünü uygulayan herhangi bir tür, bildirimleri almak için abone olabilir:
type EmailAlert struct{ Address string }
func (e EmailAlert) Update(event string) {
fmt.Printf("Email to %s: %s\n", e.Address, event)
}
type Logger struct{}
func (l Logger) Update(event string) {
fmt.Println("Log:", event)
}Özne, somut türlerini bilmeden tüm gözlemcileri bilgilendirir:
subject := &Subject{}
subject.Subscribe(EmailAlert{Address: "user@example.com"})
subject.Subscribe(Logger{})
subject.Notify("Order placed")
// E-posta user@example.com adresine: Sipariş verildi
// Log: Sipariş verildiBu tasarım kalıbı, özneyi gözlemcilerinden ayırarak mevcut kodu değiştirmeden yeni gözlemci türleri eklemeyi kolaylaştırır. Olay güdümlü mimarilerde, GUI çerçevelerinde ve yayınla/abone ol mesajlaşma sistemlerinde yaygın olarak kullanılır.
Görev
KolayObserver desenini kullanarak bir hisse senedi fiyatı uyarı sistemi oluşturalım! Bir hisse senedinin fiyatı her değiştiğinde birden çok uyarı hizmetinin otomatik olarak bildirim aldığı bir sistem oluşturacaksın. Bu, subject'in somut türlerini bilmeden observer'ların event'lere nasıl tepki verebildiğini göstermek için idealdir.
Kodunu üç dosyaya ayıracaksın:
observer.go: observer interface'ini ve somut observer türlerini Define et.Fiyatlar değiştiğinde hisse senedi bilgilerini alan
Update(stockName string, price float64)method'una sahip birObserverinterface'i Create et.İki observer türü Implement et:
PriceAlert; birThresholdalanına (float64) sahip olmalı ve güncellendiğinde, fiyat eşikten yüksekseALERT: [stockName] at $[price] crossed threshold $[threshold], düşükseWATCHING: [stockName] at $[price] (threshold: $[threshold])döndürmelidirPriceLogger; birLogNamealanına (string) sahip olmalı ve güncellendiğinde[logName] recorded [stockName]: $[price]döndürmelidir
Her iki observer da biçimlendirilmiş yanıt metnini döndüren bir method'a sahip olmalıdır.
stock.go: observer'ları yöneten ve fiyat değişikliklerini onlara bildiren subject'i Create et.Name(string),Price(float64) alanlarına ve bir observer dilimine sahip birStockstruct'ı oluştur. Aşağıdaki method'ları Add et:- Bir observer eklemek için
Subscribe(o Observer) - Fiyatı güncelleyen, tüm observer'lara bildirim gönderen ve tüm yanıtlarının bir dilimini döndüren
SetPrice(price float64) []string
Ayrıca bir
NewStock(name string, initialPrice float64) *Stockconstructor'ı oluştur.- Bir observer eklemek için
main.go: Stock'u observer'larla kur ve fiyat değişikliklerini simüle et.Hisse senedi adını ve başlangıç fiyatını oku. Ardından oluşturulacak observer sayısını oku. Her observer için türünü (
alertveyalogger) ve configuration değerini (alert'ler için eşik, logger'lar için log adı) oku. Her observer'ı Stock'a Subscribe et.Son olarak yeni bir fiyat oku, Stock'u güncelle ve her observer'ın yanıtını kendi satırına yazdır.
Aşağıdaki girdiler sağlanacaktır:
- 1. satır: Hisse senedi adı
- 2. satır: Başlangıç fiyatı
- 3. satır: Observer sayısı
- Her observer için: tür (
alertveyalogger), ardından configuration değeri - Son satır: Ayarlanacak yeni fiyat
Tüm fiyatları iki ondalık basamakla Format et.
Örneğin, şu girdi verildiğinde:
GOOG
150.00
3
alert
155.00
logger
TradeLog
alert
140.00
160.50Çıktın şöyle olmalıdır:
ALERT: GOOG at $160.50 crossed threshold $155.00
TradeLog recorded GOOG: $160.50
ALERT: GOOG at $160.50 crossed threshold $140.00Ve şu girdi verildiğinde:
AAPL
180.00
2
logger
DailyLog
alert
200.00
175.25Çıktın şöyle olmalıdır:
DailyLog recorded AAPL: $175.25
WATCHING: AAPL at $175.25 (threshold: $200.00)Stock'un alert'lere mi yoksa logger'lara mı bildirim gönderdiğini bilmediğine dikkat et. Yalnızca her observer üzerinde Update çağrısı yapar. Bu ayrıştırma, Stock kodunu değiştirmeden yeni observer türleri eklemeyi kolaylaştırır!
Kendin dene
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Stok adını oku
stockName, _ := reader.ReadString('\n')
stockName = strings.TrimSpace(stockName)
// Başlangıç fiyatını oku
initialPriceStr, _ := reader.ReadString('\n')
initialPrice, _ := strconv.ParseFloat(strings.TrimSpace(initialPriceStr), 64)
// Gözlemci sayısını oku
numObserversStr, _ := reader.ReadString('\n')
numObservers, _ := strconv.Atoi(strings.TrimSpace(numObserversStr))
// TODO: NewStock yapıcısını kullanarak yeni bir Stock oluştur
// TODO: Her gözlemci için döngü yap
for i := 0; i < numObservers; i++ {
// Gözlemci türünü oku ("alert" veya "logger")
observerType, _ := reader.ReadString('\n')
observerType = strings.TrimSpace(observerType)
// Yapılandırma değerini oku
configValue, _ := reader.ReadString('\n')
configValue = strings.TrimSpace(configValue)
// TODO: Türe göre uygun gözlemciyi oluştur
// - "alert" için: configValue değerini float64 eşik olarak ayrıştır, PriceAlert oluştur
// - "logger" için: configValue değerini LogName olarak kullan, PriceLogger oluştur
// TODO: Gözlemciyi stoka abone et
}
// Yeni fiyatı oku
newPriceStr, _ := reader.ReadString('\n')
newPrice, _ := strconv.ParseFloat(strings.TrimSpace(newPriceStr), 64)
// TODO: Yeni fiyatı ayarla ve yanıtları al
// TODO: Her yanıtı kendi satırına yazdır
}
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
1Go OOP Temelleri
Harici DosyalarGo Çalışma Alanı ve ModüllerPaketler ve İçe AktarmalarDışa Aktarılan ve Aktarılmayan İsimlerGo'da OOP'ye GirişSınıf Olarak Struct'larStruct'larda Metot TanımlamaPointer ve Değer AlıcılarStruct BaşlatmaYapıcı FonksiyonlarÖzet - Basit Hesap Makinesi4Arayüzler
Arayüzlere GirişÖrtük UygulamaSözleşme Olarak ArayüzBoş Arayüz (any)Tür OnaylamaTür SeçimiArayüz BileşimiStringer ve Error ArayüzleriÖzet - Şekil Hesaplayıcı7Kapsülleme
Dışa Aktarılan ve Aktarılmayan AlanlarPaket Seviyesinde KapsüllemeGetter ve Setter MetotlarıGo'da Bilgi GizlemeÖzet - Öğrenci Kayıtları10Generics (Go 1.18+)
Generics'e GirişTür ParametreleriTür KısıtlamalarıGeneric Struct'larGeneric Metotlar İçin Geçici ÇözümÖzet - Generic Koleksiyon13Tasarım Kalıpları Bölüm 1
Tasarım Kalıplarına GirişSingleton KalıbıFactory KalıbıAbstract Factory KalıbıObserver KalıbıStrategy Kalıbı2Türler ve Struct'lar: Derinlemesine İnceleme
Temel ve Bileşik TürlerÖzel Tür TanımlamalarıStruct EtiketleriAnonim Struct'larİç İçe Geçmiş Struct'larSıfır Değerler ve VarsayılanlarÖzet - Rehber5Kalıtım Yerine Kompozisyon
Go'da Neden Kalıtım YokStruct Gömme TemelleriMetot YükseltmeBirden Fazla Struct GömmeGömme vs AgregasyonGömülü Metotları GölgelemeÖzet - Çalışan Hiyerarşisi8Hata Yönetimi ve OOP
error ArayüzüÖzel Hata TürleriHata Sarmalama (fmt.Errorf)Sentinel Hatalarerrors.Is() ve errors.As()Panic, Defer ve RecoverÖzet - Dosya Ayrıştırıcı3İşaretçiler ve Bellek
Go'da İşaretçi TemelleriStruct İşaretçileriDeğer ile Geçme ve Referans ile Geçmenew() FonksiyonuGo'da Garbage CollectionÖzet - Linked List Oluşturucu6Go'da Polimorfizm
Interface'ler ile PolimorfizmGo'da Duck TypingInterface Karşılama KurallarıPolimorfik KoleksiyonlarDependency InjectionÖzet - Payment Processor9Eşzamanlılık ve OOP
Goroutine TemelleriKanallar ve İletişimTamponlu ve Tamponsuz KanallarSelect İfadesisync.Mutex ve sync.RWMutexsync.WaitGroupThread-Safe Struct TasarımıÖzet - Worker PoolKendi başına pratik yap: Online Go derleyicisi