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، با ایجاد یک ایستگاه هواشناسی و چندین ناظر، نحوهٔ ثبت، حذف و اطلاعرسانی به ناظرها نمایش داده شده است.