Menu
Coddy logo textTech

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 bir ilişki tanımlar. Bir nesne (subject) durum değiştirdiğinde, tüm bağımlıları (observers) otomatik olarak bilgilendirilir. Bu; olay sistemleri, bildirimler veya birden fazla bileşenin değişikliklere tepki vermesi gereken her türlü senaryo için idealdir.

Go'da, bu deseni gözlemciler için bir arayüz ve abonelikleri yöneten bir konu yapısı (struct) 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, bildirim 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)
}

Subject, 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")
// user@example.com adresine e-posta: Order placed
// Log: Order placed

Bu desen, ö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 (pub/sub) mesajlaşma sistemlerinde yaygın olarak kullanılır.

challenge icon

Görev

Kolay

Observer desenini kullanarak bir hisse senedi fiyat alarm sistemi kuralım! Birden fazla alarm servisinin, bir hisse senedi fiyatı her değiştiğinde otomatik olarak bildirim aldığı bir sistem oluşturacaksınız; bu, gözlemcilerin (observers), konunun (subject) onların somut türlerini bilmesine gerek kalmadan olaylara nasıl tepki verebileceğini göstermek için mükemmeldir.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • observer.go: Gözlemci arayüzünüzü ve somut gözlemci türlerinizi tanımlayın.

    Fiyatlar değiştiğinde hisse senedi bilgilerini alan Update(stockName string, price float64) metoduna sahip bir Observer arayüzü oluşturun.

    İki gözlemci türü uygulayın:

    • Threshold alanına (float64) sahip PriceAlert — güncellendiğinde, fiyat eşiğin üzerindeyse ALERT: [stockName] at $[price] crossed threshold $[threshold], altındaysa WATCHING: [stockName] at $[price] (threshold: $[threshold]) döndürür.
    • LogName alanına (string) sahip PriceLogger — güncellendiğinde [logName] recorded [stockName]: $[price] döndürür.

    Her iki gözlemci de biçimlendirilmiş yanıt dizelerini döndüren bir metoda sahip olmalıdır.

  • stock.go: Gözlemcileri yöneten ve fiyat değişikliklerini onlara bildiren konunuzu (subject) oluşturun.

    Name (string), Price (float64) ve bir gözlemci dilimi (slice) için alanları olan bir Stock yapısı oluşturun. Şu metodları ekleyin:

    • Bir gözlemci eklemek için Subscribe(o Observer)
    • Fiyatı güncelleyen, tüm gözlemcileri bilgilendiren ve tüm yanıtlarının bir dilimini döndüren SetPrice(price float64) []string

    Ayrıca bir NewStock(name string, initialPrice float64) *Stock yapıcı fonksiyonu (constructor) oluşturun.

  • main.go: Hisse senedinizi gözlemcilerle kurun ve fiyat değişikliklerini simüle edin.

    Hisse senedi adını ve başlangıç fiyatını okuyun. Ardından oluşturulacak gözlemci sayısını okuyun. Her gözlemci için türünü (alert veya logger) ve yapılandırmasını (alarmlar için eşik, loglayıcılar için log adı) okuyun. Her gözlemciyi hisse senedine abone yapın.

    Son olarak, yeni bir fiyat okuyun, hisse senedini güncelleyin ve her gözlemcinin yanıtını kendi satırına yazdırın.

Aşağıdaki girdiler sağlanacaktır:

  • Satır 1: Hisse senedi adı
  • Satır 2: Başlangıç fiyatı
  • Satır 3: Gözlemci sayısı
  • Her gözlemci için: tür (alert veya logger), ardından yapılandırma değeri
  • Son satır: Ayarlanacak yeni fiyat

Tüm fiyatları iki ondalık basamakla biçimlendirin.

Örneğin, şu girdi verildiğinde:

GOOG
150.00
3
alert
155.00
logger
TradeLog
alert
140.00
160.50

Çıktınız şö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.00

Ve şu girdi verildiğinde:

AAPL
180.00
2
logger
DailyLog
alert
200.00
175.25

Çıktınız şöyle olmalıdır:

DailyLog recorded AAPL: $175.25
WATCHING: AAPL at $175.25 (threshold: $200.00)

Hisse senedinin (Stock), alarmları mı yoksa loglayıcıları mı bilgilendirdiğini bilmediğine dikkat edin; sadece her gözlemci üzerinde Update metodunu çağırır. Bu ayrıştırma (decoupling), Stock kodunu değiştirmeden yeni gözlemci türleri eklemeyi kolaylaştırır!

Kopya kağıdı

Observer deseni, bir öznenin (subject) durumu değiştiğinde birden fazla gözlemciyi (observer) otomatik olarak bilgilendirdiği bire-çok bir ilişki tanımlar. Bu, özneyi gözlemcilerinden ayırarak (decoupling) olay sistemleri ve bildirimler için ideal hale getirir.

Temel Yapı

Bir gözlemci arayüzü ve abonelikleri yöneten bir özne tanımlayın:

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)
    }
}

Gözlemcileri Uygulama

Observer arayüzünü uygulayan herhangi bir tür 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)
}

Kullanım

Gözlemcileri abone yapın ve değişiklikleri onlara bildirin:

subject := &Subject{}
subject.Subscribe(EmailAlert{Address: "user@example.com"})
subject.Subscribe(Logger{})

subject.Notify("Order placed")
// Email to user@example.com: Order placed
// Log: Order placed

Özne, somut türlerini bilmeden tüm gözlemcileri bilgilendirir; bu da mevcut kodu değiştirmeden yeni gözlemci türleri eklemeyi kolaylaştırır.

Kendin dene

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Hisse senedi 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 kurucusunu kullanarak yeni bir Stock oluştur

	// TODO: Her bir gözlemciyi döngüye al
	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 değeri olarak ayrıştır, PriceAlert oluştur
		// - "logger" için: configValue değerini LogName olarak kullan, PriceLogger oluştur
		// TODO: Gözlemciyi hisse senedine abone yap
	}

	// Yeni fiyatı oku
	newPriceStr, _ := reader.ReadString('\n')
	newPrice, _ := strconv.ParseFloat(strings.TrimSpace(newPriceStr), 64)

	// TODO: Yeni fiyatı ayarla ve yanıtları al

	// TODO: Her bir yanıtı kendi satırına yazdır
}
quiz iconKendini test et

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