设计模式 #2:观察者模式
Posted on 2016-7-12 in Code
概括
观察者模式 = 发布者(publisher) + 观察者(observer / subscriber)
模式定义
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
例子:气象监测应用
建立下一代 Internet 气象观察站。该气象站必须建立在 WeatheData 对象上,由 WeatherData 对象负责追踪目前的天气状况(温度、湿度、气压)。 对问题进行分析,发现我们要做的是: 建立一个应用,有三种布告板,分别显示目前的状况、气象统计及简单的预报。 当 WeatherObject 对象获得最新的测量数据时,三种布告板必须实时更新。 * 系统必须可扩展,让其他开发人员可以建立定制的布告板。
解决方案1
subject.py
class Subject(object):
'''Subject interface class'''
def register_observer(self, observer):
raise NotImplementedError('abstract Subject')
def remove_observer(self, observer):
raise NotImplementedError('abstract Subject')
def notify_observers(self):
raise NotImplementedError('abstract Subject')
weather_data.py
from subject import Subject
class WeatherData(Subject):
def __init__(self):
self.observers = []
self.temperature = 0.0
self.humidity = 0.0
self.pressure = 0.0
def register_observer(self, observer):
'''If need to register observer, just add it to observers list
'''
self.observers.append(observer)
def remove_observer(self, observer):
'''The same, if observer wants to unregister, we will remove
him from observer list.'''
if observer in self.observers:
self.observers.remove(observer)
def notify_observers(self):
'''We sent statement to every observers. Since all observers
implement update() method, we know how to notify them.'''
for observer in self.observers:
observer.update(self.temperature, self.humidity, self.pressure)
def measurements_changes(self):
'''We need to notify observers when we get measurements from
Weather-O-Rama company.'''
self.notify_observers()
def set_measurements(self, temperature, humidity, pressure):
'''We will use this method to test billboard.'''
self.temperature = temperature
self.humidity = humidity
self.pressure = pressure
self.measurements_changes()
# Other methods in WeatherData
observer.py
class Observer(object):
'''Observer interface class'''
def update(self, temperature, humidity, pressure):
raise NotImplementedError('abstract Observer')
display_element.py
from weather_data import WeatherData
from observer import Observer
class DisplayElement(object):
'''DisplayElement interface class'''
def display(self):
raise NotImplementedError('abstract DisplayElement')
class CurrentConditionDisplay(Observer, DisplayElement):
'''Billboard class'''
def __init__(self, weather_data):
self.weather_data = weather_data
weather_data.register_observer(self)
def update(self, temperature, humidity, pressure):
self.temperature = temperature
self.humidity = humidity
self.display()
def display(self):
print "Current conditions: %.1fF degrees and %.1f%% humidity" % (
self.temperature, self.humidity
)
解决方案2
对于解决方案1而言,存在两个问题:
- 当主题对象更新的时候,所有的观察者对象都必须更新,对于观察者而言,可能并不想在执行一些重要的任务的时候,收到此类更新。
- 对于主题对象发送给观察者对象的数据,并不能满足所有观察者的需求,对于有些观察者而言,可能只需要一点点的数据。
所以,我们需要:
- 主题对象可以提供
getter
方法让观察者对象主动拉取数据。 - 增加
set_changed()
方法来标记主题对象状态已经改变的事实,增加notify_observers()
方法来通知观察者。- 先调用
set_changed()
标记状态已经改变的事实。 - 调用
notify_observers()
通知观察者。
- 先调用
from subject import Subject
class WeatherData(Subject):
def __init__(self):
self.observers = []
self.temperature = 0.0
self.humidity = 0.0
self.pressure = 0.0
def register_observer(self, observer):
'''If need to register observer, just add it to observers list
'''
self.observers.append(observer)
def remove_observer(self, observer):
'''The same, if observer wants to unregister, we will remove
him from observer list.'''
if observer in self.observers:
self.observers.remove(observer)
def measurements_changed(self):
self.set_changed()
self.notify_observers() # We do not pass data, means use pull mode
def set_changed(self):
self.changed = True
def notify_observers(self):
'''We sent statement to every observers. Since all observers
implement update() method, we know how to notify them.'''
if self.changed:
for observer in self.observers:
observer.update()
self.changed = False
def measurements_changes(self):
'''We need to notify observers when we get measurements from
Weather-O-Rama company.'''
self.notify_observers()
def set_measurements(self, temperature, humidity, pressure):
'''We will use this method to test billboard.'''
self.temperature = temperature
self.humidity = humidity
self.pressure = pressure
self.measurements_changed()
# Below three are examples of getter methods
def get_temperature(self):
return self.temperature
def get_humidity(self):
return self.humidity
def get_pressure(self):
return self.pressure
# Other methods in WeatherData