往期回顾:
设计气象观测站
以书中的气象监测应用为例:现在有一个气象中心可以监测温度、湿度、气压三种数据,我们需要通过 WeatherData
对象来获取这些数据,然后将这些数据显示在特定的装置上。

WeatherData
拥有以下方法:
getTemperature()
:获取温度数据
getHumidity()
: 获取湿度数据
getPressure()
:获取气压数据
measurementsChanged()
:一旦气象站更新数据,这个方法会被调用
这样一看似乎十分简单:我们只要在 measurementsChanged()
中通过一系列 getter
获取到气象台提供的温度、湿度与气压数据,然后再调用显示装置的更新数据方法即可。
但是,如果我们后续需要增加或减少显示装置应该怎么办呢?每次都要修改 measurementsChanged()
显然不是个好办法。
出版者与订阅者
想想在现实生活中我们是怎么享受报纸订阅服务的?
- 报社负责出版报纸,可以接受人们的订阅或取消订阅
- 如果我们向报社订阅了报纸,一旦有新报纸出版,报社就会送来新的报纸
- 如果我们不想看报纸了,就取消订阅,报社就不会再送新报纸上门
气象站与显示装置之间其实也是这样的关系,气象站为「出版者」,显示装置为「订阅者」:需要获得气象站数据的显示装置可以向气象站申请「订阅」,这样一旦有气象数据更新,气象站就会通知申请订阅的显示装置;如果显示装置不再需要该气象站提供数据,则可以「取消订阅」,不再接受气象站的通知。
上述「出版者」称为「主题」(Subject),「订阅者」称为「观察者」(Observer),两者构成了观察者模式的主要部分。
定义观察者模式
观察者模式定义如下:
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并且自动更新。

类图中包含两个接口定义:
- 主题接口
Subject
:
registerObserver()
:添加订阅者
removeObserver()
:移除订阅者
notifyObserver()
:通知订阅者
- 观察者接口
Observer
:
update()
:在主题 notifyObserver()
中被调用,用于更新观察者的数据
实现气象站
设计类图
根据上述观察者模式定义,我们先为气象站设计「主题」与「观察者」两个接口,除此之外也可以添加一个显示装置接口,专门负责显示装置的具体显示格式。
接口定义好后,就可以让具体的类来实现这些接口了:
WeatherData
作为具体主题,实现 Subject
主题接口
- 各个显示装置作为具体观察者,实现
Observer
观察者接口

具体实现
PHP
<?php
interface Subject { public function registerObserver($observer);
public function removeObserver($observer);
public function notifyObservers(); }
interface Observer { public function update($temp, $humidity, $pressure); }
interface DisplayElement { public function display(); }
class WeatherData implements Subject { private $observers;
private $temperature;
private $humidity;
private $pressure;
public function __construct() { $this->observers = []; }
public function registerObserver($observer) { $this->observers[] = $observer; }
public function removeObserver($observer) { $index = array_search($observer, $this->observers); unset($this->observers[$index]); }
public function notifyObservers() { foreach ($this->observers as $observer) { $observer->update($this->temperature, $this->humidity, $this->pressure); } }
public function measurementsChanged() { $this->notifyObservers(); }
public function setMeasurements($temperature, $humidity, $pressure) { $this->temperature = $temperature; $this->humidity = $humidity; $this->pressure = $pressure; $this->measurementsChanged(); } }
class FirstDisplay implements Observer, DisplayElement { private $temperature;
private $humidity;
private $weatherData;
public function __construct($weatherData) { $this->weatherData = $weatherData; $this->weatherData->registerObserver($this); }
public function update($temperature, $humidity, $pressure) { $this->temperature = $temperature; $this->humidity = $humidity; $this->display(); }
public function display() { echo "当前温度:{$this->temperature},当前湿度:{$this->humidity}\n"; } }
class SecondDisplay implements Observer, DisplayElement {
private $pressure;
private $weatherData;
public function __construct($weatherData) { $this->weatherData = $weatherData; $this->weatherData->registerObserver($this); }
public function update($temperature, $humidity, $pressure) { $this->pressure = $pressure; $this->display(); }
public function display() { echo "当前气压:{$this->pressure}\n"; } }
$weatherData = new WeatherData();
$firstDisplay = new FirstDisplay($weatherData);
$weatherData->setMeasurements(80, 70, 30.4); $weatherData->setMeasurements(70, 60, 29.2);
$weatherData->removeObserver($firstDisplay);
$secondDisplay = new SecondDisplay($weatherData); $weatherData->setMeasurements(90, 60, 29.2);
|
Python
class Subject: def __init__(self): """ 主题(出版者) """ self._observers = [] def register(self, observer): """ 添加观察者 """ if observer not in self._observers: self._observers.append(observer)
def remove(self, observer): """ 移除观察者 """ try: self._observers.remove(observer) except ValueError: pass
def notify(self): """ 发送通知给所有观察者 """ for observer in self._observers: observer.update()
class WeatherData(Subject): def __init__(self): Subject.__init__(self) self._temperature = 0 self._humidity = 0 self._pressure = 0
def set_measurements(self, temperature, humidity, pressure): """ 气象数据发生变动时调用该函数 """ self._temperature = temperature self._humidity = humidity self._pressure = pressure self.notify()
@property def temperature(self): return self._temperature @property def humidity(self): return self._humidity @property def pressure(self): return self._pressure
class FirstDisplay: def __init__(self, weatherData): """ 显示装置 1:显示温度和湿度 """ self._weather_data = weatherData self._weather_data.register(self) self._temperature = 0 self._humidity = 0 def update(self): """ 更新数据 """ self._temperature = self._weather_data.temperature self._humidity = self._weather_data.humidity self.display() def display(self): """ 显示数据 """ print("当前温度:%s,当前湿度:%s" % (self._temperature, self._humidity))
class SecondDisplay: def __init__(self, weatherData): """ 显示装置 2:显示气压 """ self._weather_data = weatherData self._weather_data.register(self) self._pressure = 0
def update(self): """ 更新数据 """ self._pressure = self._weather_data.pressure self.display()
def display(self): print("当前气压:%s" % self._pressure)
def main(): weather_data = WeatherData() first_display = FirstDisplay(weather_data) weather_data.set_measurements(21, 50, 3) weather_data.set_measurements(3, 70, 4) weather_data.remove(first_display) second_display = SecondDisplay(weather_data) weather_data.set_measurements(21, 50, 30)
if __name__ == "__main__": main()
""" Output: 当前温度:21,当前湿度:50 当前温度:3,当前湿度:70 当前气压:30 """
|
Golang
package main
import ( "fmt" )
type Subject interface { Register(observer Observer) Remove(obeserver Observer) Notify() }
type WeatherData struct { observers []Observer temperature int humidity int pressure float32 }
func (w *WeatherData) Register(observer Observer) { w.observers = append(w.observers, observer) }
func (w *WeatherData) Remove(observer Observer) { j := 0 for _, ob := range w.observers { if ob != observer { w.observers[j] = observer j++ } } w.observers = w.observers[:j] }
func (w *WeatherData) Notify() { for _, observer := range w.observers { observer.update(w.temperature, w.humidity, w.pressure) } }
func (w *WeatherData) SetMeasurements(temperature int, humidity int, pressure float32) { w.temperature = temperature w.humidity = humidity w.pressure = pressure w.Notify() }
type Observer interface { update(temperature int, humidity int, pressure float32) display() }
type FirstDisplay struct { temperature int humidity int pressure float32 }
func (display *FirstDisplay) update(temperature int, humidity int, pressure float32) { display.temperature = temperature display.humidity = humidity display.pressure = pressure display.display() }
func (display *FirstDisplay) display() { fmt.Printf("当前温度:%d, 当前湿度:%d\n", display.temperature, display.humidity) }
type SecondDisplay struct { temperature int humidity int pressure float32 }
func (display *SecondDisplay) update(temperature int, humidity int, pressure float32) { display.temperature = temperature display.humidity = humidity display.pressure = pressure display.display() }
func (display *SecondDisplay) display() { fmt.Printf("当前气压:%.2f\n", display.pressure) }
func main() { weatherData := WeatherData{} firstDisplay := &FirstDisplay{} weatherData.Register(firstDisplay) weatherData.SetMeasurements(23, 50, 23.1) secondDisplay := &SecondDisplay{} weatherData.Register(secondDisplay) weatherData.SetMeasurements(22, 70, 24.2) }
|
总结
- 观察者模式定义了对象之间一对多的关系
- 主题通过一个共同的接口来更新观察者
- 主题和观察者之间用松耦合方式结合,主题不需要知道观察者的细节,具体观察者只需要实现观察者的接口