【观察者模式】
观察者模式是软体设计模式的一种。在此种模式中，一个目标物件管理所有相依于它的观察者物件，并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用在即时事件处理系统。
== 结构 ==
File:Observer-pattern-class-diagram.png
== 参与类别 ==
参与本模式的各类别列出如下。成员函式以模拟的方式列出。
=== 抽象目标类别 ===
此抽象类别提供一个界面让观察者进行添附与解附作业。此类别内有个不公开的观察者串炼，并透过下列函式(方法)进行作业
* 添附(Attach)：新增观察者到串炼内，以追踪目标物件的变化。
* 解附(Detach)：将已经存在的观察者从串炼中移除。
* 通知(Notify)：利用观察者所提供的更新函式来通知此目标已经产生变化。
添附函式包涵了一个观察者物件参数。也许是观察者类别的虚拟函式(即更新函式)，或是在非物件导向的设定中所使用的函式指标(更广泛来讲，函式子或是函式物件)。
=== 目标类别 ===
此类别提供了观察者欲追踪的状态。也利用其源类别(例如前述的抽象目标类别)所提供的方法,来通知所有的观察者其状态已经更新。此类别拥有以下函式
* 取得状态(GetState)：回传该目标物件的状态。
=== 抽象观察者介面 ===
抽象观察者类别是一个必须被实做的抽象类别。这个类别定义了所有观察者都拥有的更新用介面，此介面是用来接收目标类别所发出的更新通知。此类别含有以下函式
* 更新(Update)：会被实做的一个抽象(虚拟)函式。
=== 观察者类别 ===
这个类别含有指向目标类别的参考(reference)，以接收来自目标类别的更新状态。此类别含有以下函式
* 更新(Update)：是前述抽象函式的实做。当这个函式被目标物件呼叫时，观察者物件将会呼叫目标物件的取得状态函式，来其所拥有的更新目标物件资讯。
每个观察者类别都要实做它自己的更新函式，以应对状态更新的情形。
当目标物件改变时，会通过呼叫它自己的通知函式来将通知送给每一个观察者物件，这个通知函式则会去呼叫已经添附在串炼内的观察者更新函式。通知与更新函式可能会有一些参数，好指明是目前目标物件内的何种改变。这么作将可增进观察者的效率(只更新那些改变部份的状态)。
== 用途 ==
* 当抽象个体有两个互相依赖的层面时。封装这些层面在单独的物件内将可允许程式设计师单独地去变更与重复使用这些物件，而不会产生两者之间交互的问题。
* 当其中一个物件的变更会影响其他物件，却又不知道多少物件必须被同时变更时。
* 当物件应该有能力通知其他物件，又不应该知道其他物件的实做细节时。
观察者模式通常与 MVC 范式有关系。在 MVC 中，观察者模式被用来降低 model 与 view 的耦合程度。一般而言， model 的改变会触发通知其他身为观察者的 model 。而这些 model 实际上是 view 。 Java Swing 就是个范例，示意了 model 预期会透过 PropertyChangeNotification 架构以送出改变的通知给其他 view 。 Model 类别是 Java bean 类别的一员，并拥有与上述目标类别同样的行为。 View 类别则系结了一些 GUI 中的可视元素，并拥有与上述观察者类别同样的行为。当应用程式在执行时。使用者将因 view 做出相应的更新而看见 model 所产生的变更。
== 示例 ==
=== Python ===
class AbstractSubject(object):
def register(self, listener):
raise NotImplementedError("Must subclass me")
def deregister(self, listener):
raise NotImplementedError("Must subclass me")
def notify_listeners(self, event):
raise NotImplementedError("Must subclass me")
class Listener(object):
def __init__(self, name, subject):
self.name = name
subject.register(self)
def notify(self, event):
print(self.name, "received event", event)
class Subject(AbstractSubject):
def __init__(self):
self.listeners =
self.data = None
def getUserAction(self):
self.data = raw_input('Enter something to do:')
return self.data
# Implement abstract Class AbstractSubject
def register(self, listener):
self.listeners.append(listener)
def deregister(self, listener):
self.listeners.remove(listener)
def notify_listeners(self, event):
for listener in self.listeners:
listener.notify(event)
if __name__=="__main__":
# make a subject object to spy on
subject = Subject()
# register two listeners to monitor it.
listenerA = Listener("", subject)
listenerB = Listener("", subject)
# simulated event
subject.notify_listeners ("")
# outputs:
#      received event
#      received event
action = subject.getUserAction()
subject.notify_listeners(action)
#Enter something to do:hello
# outputs:
#      received event hello
#      received event hello
=== C# ===
C#和其他使用.NET Framework的语言一般无需使用接口和类实现典型的观察者模式，但是这里依然给一个例子。
using System;
using System.Collections;
namespace Wikipedia.Patterns.Strategy
{
// IObserver --> interface for the observer
public interface IObserver
{
// called by the subject to update the observer of any change
// The method parameters can be modified to fit certain criteria
void Update(string message);
}
public class Subject
{
// use array list implementation for collection of observers
private ArrayList observers;
// constructor
public Subject()
{
observers = new ArrayList();
}
public void Register(IObserver observer)
{
// if list does not contain observer, add
if (!observers.Contains(observer))
{
observers.Add(observer);
}
}
public void Deregister(IObserver observer)
{
// if observer is in the list, remove
if (observers.Contains(observer))
{
observers.Remove(observer);
}
}
public void Notify(string message)
{
// call update method for every observer
foreach (IObserver observer in observers)
{
observer.Update(message);
}
}
}
// Observer1 --> Implements the IObserver
public class Observer1 : IObserver
{
public void Update(string message)
{
Console.WriteLine("Observer1:" + message);
}
}
// Observer2 --> Implements the IObserver
public class Observer2 : IObserver
{
public void Update(string message)
{
Console.WriteLine("Observer2:" + message);
}
}
// Test class
public class ObserverTester
{
STAThread
public static void Main()
{
Subject mySubject = new Subject();
IObserver myObserver1 = new Observer1();
IObserver myObserver2 = new Observer2();
// register observers
mySubject.Register(myObserver1);
mySubject.Register(myObserver2);
mySubject.Notify("message 1");
mySubject.Notify("message 2");
}
}
}
=== C++ ===
#include
#include
#include
#include
using namespace std;
// The Abstract Observer
class ObserverBoardInterface
{
public:
virtual void update(float a,float b,float c) = 0;
};
// Abstract Interface for Displays
class DisplayBoardInterface
{
public:
virtual void show() = 0;
};
// The Abstract Subject
class WeatherDataInterface
{
public:
virtual void registerob(ObserverBoardInterface* ob) = 0;
virtual void removeob(ObserverBoardInterface* ob) = 0;
virtual void notifyOb() = 0;
};
// The Concrete Subject
class ParaWeatherData: public WeatherDataInterface
{
public:
void SensorDataChange(float a,float b,float c)
{
m_humidity = a;
m_temperature = b;
m_pressure = c;
notifyOb();
}
void registerob(ObserverBoardInterface* ob)
{
m_obs.push_back(ob);
}
void removeob(ObserverBoardInterface* ob)
{
m_obs.remove(ob);
}
protected:
void notifyOb()
{
list::iterator pos = m_obs.begin();
while (pos != m_obs.end())
{
((ObserverBoardInterface* )(*pos))->update(m_humidity,m_temperature,m_pressure);
(dynamic_cast(*pos))->show();
++pos;
}
}
private:
float        m_humidity;
float        m_temperature;
float        m_pressure;
list m_obs;
};
// A Concrete Observer
class CurrentConditionBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
CurrentConditionBoard(WeatherDataInterface& a):m_data(a)
{
m_data.registerob(this);
}
void show()
{
coutm_maxt)
{
m_maxt = t;
}
if (tSensorDataChange(10.2, 28.2, 1001);
wdata->SensorDataChange(12, 30.12, 1003);
wdata->SensorDataChange(10.2, 26, 806);
wdata->SensorDataChange(10.3, 35.9, 900);
wdata->removeob(currentB);
wdata->SensorDataChange(100, 40, 1900);
delete statisticB;
delete currentB;
delete wdata;
return 0;
}
=== PHP ===
class STUDENT
tipo;
}
public function GET_nome() {
return $this->nome;
}
public function GET_email() {
return $this->email;
}
public function GET_telefone() {
return $this->nome;
}
function __construct($nome) {
$this->nome = $nome;
}
public function update(SplSubject $object){
$object->SET_log("Comes from ".$this->nome.": I'm a student of ".$object->GET_materia());
}
}
?>
class TEACHER
tipo;
}
public function GET_nome() {
return $this->nome;
}
public function GET_email() {
return $this->email;
}
public function GET_telefone() {
return $this->nome;
}
function __construct($nome) {
$this->nome = $nome;
}
public function update(SplSubject $object){
$object->SET_log("Comes from ".$this->nome.": I teach in ".$object->GET_materia());
}
}
?>
Class SUBJECT
nome_materia;
}
function SET_log($valor) {
$this->_log = $valor ;
}
function GET_log() {
return $this->_log;
}
function __construct($nome) {
$this->nome_materia = $nome;
$this->_log = " Subject $nome was included";
}
/* Adiciona um observador */
public function attach(SplObserver $classes) {
$this->_classes = $classes;
$this->_log = " The ".$classes->GET_tipo()." ".$classes->GET_nome()." was included";
}
/* Remove um observador */
public function detach(SplObserver $classes) {
foreach ($this->_classes as $key => $obj) {
if ($obj == $classes) {
unset($this->_classes$key);
$this->_log = " The ".$classes->GET_tipo()." ".$classes->GET_nome()." was removed";
}
}
}
/* Notifica os observadores */
public function notify(){
foreach ($this->_classes as $classes) {
$classes->update($this);
}
}
}
?>
Application
attach($rafael);
$subject->attach($vinicius);
$subject->attach($marcus);
$subject2 = new Subject("English");
$renato = new Teacher("Renato");
$fabio = new Student("Fabio");
$tiago = new Student("tiago");
// Include observers in the english Subject
$subject2->attach($renato);
$subject2->attach($vinicius);
$subject2->attach($fabio);
$subject2->attach($tiago);
// Remove the instance "Rafael from subject"
$subject->detach($rafael);
// Notify both subjects
$subject->notify();
$subject2->notify();
echo "First Subject ";
echo "";
print_r($subject->GET_log());
echo "";
echo "";
echo "Second Subject ";
echo "";
print_r($subject2->GET_log());
echo "";
?>
OUTPUT
First Subject
Array
(
0 =>  Subject Math was included
1 =>  The Student Rafael was included
2 =>  The Student Vinicius was included
3 =>  The Teacher Marcus Brasizza was included
4 =>  The Student Rafael was removed
5 => Comes from Vinicius: I'm a student of Math
6 => Comes from Marcus Brasizza: I teach in Math
)
--------------------------------------------------------------------------------
Second Subject
Array
(
0 =>  Subject English was included
1 =>  The Teacher Renato was included
2 =>  The Student Vinicius was included
3 =>  The Student Fabio was included
4 =>  The Student tiago was included
5 => Comes from Renato: I teach in English
6 => Comes from Vinicius: I'm a student of English
7 => Comes from Fabio: I'm a student of English
8 => Comes from tiago: I'm a student of English
)
亦可参考
https://web.archive.org/web/20100330025802/http://www.javaworld.com/javaworld/javaqa/2001-05/04-qa-0525-observer.html