观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。

当新类型的观察者出现时,主题的代码不需要改变,我们需要做的是在新的类里实现观察者的接口,然后注册给主题即可。主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象。

改变主题或观察者其中一方,并不会影响另一方,因为两者是松耦合的,只要它们之间的接口仍被遵守,我们就可以自由地改变它们。

接口设计

  • 主题:

我们的主题应该至少拥有以下三个方法:

type subject interface {
    registerObserver(string, observer)
    removeObserver(string)
    notifyObservers()
}
  • 观察者

观察者需要拥有update方法,以便主题调用该方法来更新观察者

type observer interface {
    update(float64, float64, float64)
}

具体实现

假设我们有一个weatherData类,这个类用来获取气象信息,一旦气象信息被改变,就通知所有注册给weatherData观察者

type weatherData struct {
    tem       float64
    hum       float64
    pre       float64
    observers map[string]observer
}

func (w *weatherData) init() {
    w.observers = map[string]observer{}
}

func (w *weatherData) registerObserver(name string, o observer) {
    w.observers[name] = o
}

func (w *weatherData) removeObserver(name string) {
    delete(w.observers, name)
}

func (w *weatherData) notifyObservers() {
    for k, v := range w.observers {
        fmt.Printf("notify observer %+v ...\n", k)
        v.update(w.tem, w.hum, w.pre)
        fmt.Printf("notify observer %+v success\n", k)
    }
}

func (w *weatherData) measurementsChanged() {
    w.notifyObservers()
}

func (w *weatherData) setMeasurements(tem, hum, pre float64) {
    w.tem = tem
    w.hum = hum
    w.pre = pre
    w.measurementsChanged()
}

可以看到,weatherData实现了主题的所有方法,并且有一个setMeasurements方法,一旦这个方法被调用,就会通知所有注册给它的观察者

看下观察者的实现:

type displayElement interface {
    display()
}

type currentConditionDisplay struct {
    tem float64
    hum float64
    sub subject
}

func (c *currentConditionDisplay) display() {
    fmt.Printf("current condition display: temprature:%+v, humidity:%+v\n", c.tem, c.hum)
}

func (c *currentConditionDisplay) update(tem, hum, sub float64) {
    c.tem = tem
    c.hum = hum
    c.display()
}

type forecastDisplay struct {
    tem float64
    hum float64
    sub subject
}

func (f *forecastDisplay) display() {
    fmt.Printf("forecast display: temprature:%+v, humidity:%+v\n", f.tem, f.hum)
}

func (f *forecastDisplay) update(tem, hum, sub float64) {
    f.tem = tem
    f.hum = hum
    f.display()
}

实际上观察者实现了displayElement和observer两个接口,这时我们整合下代码:

wd := &weatherData{}
    wd.init()

    ccd := &currentConditionDisplay{
        sub: wd,
    }
    fd := &forecastDisplay{
        sub: wd,
    }
    ccd.sub.registerObserver("currentConditionDisplay", ccd)
    ccd.sub.registerObserver("forecastDisplay", fd)

    wd.setMeasurements(10.1, 20.2, 30.3)

运行之,结果为:

notify observer currentConditionDisplay ...
current condition display: temprature:10.1, humidity:20.2
notify observer currentConditionDisplay success
notify observer forecastDisplay ...
forecast display: temprature:10.1, humidity:20.2
notify observer forecastDisplay success

一旦注册之后,所有观察者都被通知了

总结

在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
java的swing框架中大量使用了观察者模式,很多gui框架也是如此

参考文章

《head first设计模式》


byte
106 声望13 粉丝