9.3.6 الگو Observer

9.3.6 الگو Observer

9.3.6.1 - توضیح الگوی Observer #

الگوی Observer یک الگوی طراحی رفتاری است که رابطه‌ی یک-به-چند بین اشیا را تعریف می‌کند، به طوری که وقتی یک شیء (سوژه) حالت خود را تغییر می‌دهد، تمام اشیای وابسته به آن (ناظرها) به طور خودکار مطلع و به روز می‌شوند. این الگو برای ایجاد سیستم‌های event-driven ایده‌آل است و وابستگی قوی بین سوژه و ناظرها را از بین می‌برد، زیرا سوژه نیازی ندارد بداند ناظرهایش چه اشیایی هستند و فقط با آن‌ها از طریق یک رابط مشترک تعامل می‌کند. این الگو در موقعیت‌هایی مانند سیستم‌های اطلاع‌رسانی، رابط‌های کاربری reactive و رویدادهای real-time کاربرد گسترده‌ای دارد.

9.3.6.2 - مثال عملی #

package main

import "fmt"

// Observer interface
type Observer interface {
    Update(temperature float64)
}

// Subject interface
type Subject interface {
    RegisterObserver(o Observer)
    RemoveObserver(o Observer)
    NotifyObservers()
}

// Concrete Subject - WeatherStation
type WeatherStation struct {
    temperature float64
    observers   []Observer
}

func (w *WeatherStation) RegisterObserver(o Observer) {
    w.observers = append(w.observers, o)
}

func (w *WeatherStation) RemoveObserver(o Observer) {
    for i, observer := range w.observers {
        if observer == o {
            w.observers = append(w.observers[:i], w.observers[i+1:]...)
            break
        }
    }
}

func (w *WeatherStation) NotifyObservers() {
    for _, observer := range w.observers {
        observer.Update(w.temperature)
    }
}

func (w *WeatherStation) SetTemperature(temp float64) {
    w.temperature = temp
    w.NotifyObservers() // Notify all observers when temperature changes
}

// Concrete Observer - PhoneDisplay
type PhoneDisplay struct {
    id string
}

func (p *PhoneDisplay) Update(temperature float64) {
    fmt.Printf("Phone Display %s: Temperature is %.1f°C\n", p.id, temperature)
}

// Concrete Observer - TVDisplay
type TVDisplay struct {
    channel string
}

func (t *TVDisplay) Update(temperature float64) {
    fmt.Printf("TV Display on %s: Current temperature: %.1f°C\n", t.channel, temperature)
}

func main() {
    // Create the subject (weather station)
    weatherStation := &WeatherStation{}
    
    // Create observers
    phone1 := &PhoneDisplay{id: "1"}
    phone2 := &PhoneDisplay{id: "2"}
    tv := &TVDisplay{channel: "Weather Channel"}
    
    // Register observers
    weatherStation.RegisterObserver(phone1)
    weatherStation.RegisterObserver(phone2)
    weatherStation.RegisterObserver(tv)
    
    // Change temperature - all observers get notified
    fmt.Println("Setting temperature to 25.5°C:")
    weatherStation.SetTemperature(25.5)
    
    fmt.Println("\nSetting temperature to 30.2°C:")
    weatherStation.SetTemperature(30.2)
    
    // Remove one observer
    fmt.Println("\nRemoving Phone Display 2 and setting temperature to 28.0°C:")
    weatherStation.RemoveObserver(phone2)
    weatherStation.SetTemperature(28.0)
}

در این پیاده‌سازی الگوی Observer، دو رابط اصلی Observer و Subject تعریف شده‌اند. رابط Observer تنها متد Update را دارد که برای دریافت به‌روزرسانی‌ها از سوژه استفاده می‌شود. رابط Subject نیز متدهای RegisterObserver، RemoveObserver و NotifyObservers را برای مدیریت ناظرها تعریف می‌کند. کلاس WeatherStation به عنوان سوژهٔ بتن، این رابط را پیاده‌سازی کرده و دارای یک لیست از ناظرها است. وقتی دمای ایستگاه هواشناسی با متد SetTemperature تغییر می‌کند، متد NotifyObservers فراخوانی شده و تمام ناظرهای ثبت‌شده را با مقدار جدید دما به روز می‌کند. دو ناظر بتن PhoneDisplay و TVDisplay نیز رابط Observer را پیاده‌سازی کرده و هر کدام رفتار خاص خود را در متد Update تعریف می‌کنند. در تابع main، با ایجاد یک ایستگاه هواشناسی و چندین ناظر، نحوهٔ ثبت، حذف و اطلاع‌رسانی به ناظرها نمایش داده شده است.