fbpx
维基百科

观察者模式

觀察者模式軟體設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用在即時事件處理系統。


結構 编辑

 

參與類別 编辑

參與本模式的各類別列出如下。成員函式以模擬的方式列出。

抽象目標類別 编辑

此抽象類別提供一個界面讓觀察者進行添附與解附作業。此類別內有個不公開的觀察者串鍊,並透過下列函式(方法)進行作業

  • 添附(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("<listener A>", subject) listenerB = Listener("<listener B>", subject) # simulated event subject.notify_listeners ("<event 1>") # outputs: # <listener A> received event <event 1> # <listener B> received event <event 1> action = subject.getUserAction() subject.notify_listeners(action) #Enter something to do:hello # outputs: # <listener A> received event hello # <listener B> 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 <list> #include <vector> #include <algorithm> #include <iostream> 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<ObserverBoardInterface*>::iterator pos = m_obs.begin();  while (pos != m_obs.end())  {  ((ObserverBoardInterface* )(*pos))->update(m_humidity,m_temperature,m_pressure);  (dynamic_cast<DisplayBoardInterface*>(*pos))->show();  ++pos;  }  } private:  float m_humidity;  float m_temperature;  float m_pressure;  list<ObserverBoardInterface* > m_obs; }; // A Concrete Observer class CurrentConditionBoard : public ObserverBoardInterface, public DisplayBoardInterface { public:  CurrentConditionBoard(WeatherDataInterface& a):m_data(a)  {  m_data.registerob(this);  }  void show()  {  cout<<"_____CurrentConditionBoard_____"<<endl;  cout<<"humidity: "<<m_h<<endl;  cout<<"temperature: "<<m_t<<endl;  cout<<"pressure: "<<m_p<<endl;  cout<<"_______________________________"<<endl;  }  void update(float h, float t, float p)  {  m_h = h;  m_t = t;  m_p = p;  } private:  float m_h;  float m_t;  float m_p;  WeatherDataInterface& m_data; }; // A Concrete Observer class StatisticBoard : public ObserverBoardInterface, public DisplayBoardInterface { public:  StatisticBoard(WeatherDataInterface& a):m_maxt(-1000),m_mint(1000),m_avet(0),m_count(0),m_data(a)  {  m_data.registerob(this);  }  void show()  {  cout<<"________StatisticBoard_________"<<endl;  cout<<"lowest temperature: "<<m_mint<<endl;  cout<<"highest temperature: "<<m_maxt<<endl;  cout<<"average temperature: "<<m_avet<<endl;  cout<<"_______________________________"<<endl;  }  void update(float h, float t, float p)  {  ++m_count;  if (t>m_maxt)  {  m_maxt = t;  }  if (t<m_mint)  {  m_mint = t;  }  m_avet = (m_avet * (m_count-1) + t)/m_count;  } private:  float m_maxt;  float m_mint;  float m_avet;  int m_count;  WeatherDataInterface& m_data; }; int main(int argc, char *argv[]) {    ParaWeatherData * wdata = new ParaWeatherData;  CurrentConditionBoard* currentB = new CurrentConditionBoard(*wdata);  StatisticBoard* statisticB = new StatisticBoard(*wdata);  wdata->SensorDataChange(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

<?php class Student implements SplObserver{ protected $tipo = "Student"; private $nome; private $endereco; private $telefone; private $email; private $_classes = array(); public function GET_tipo() { return $this->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

<?php class Teacher implements SplObserver{ protected $tipo = "Teacher"; private $nome; private $endereco; private $telefone; private $email; private $_classes = array(); public function GET_tipo() { return $this->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

<?php class Subject implements SplSubject { private $nome_materia; private $_observadores = array(); private $_log = array(); public function GET_materia() { return $this->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

<?php require_once("teacher.class.php"); require_once("student.class.php"); require_once("subject.class.php"); $subject = new Subject("Math"); $marcus = new Teacher("Marcus Brasizza"); $rafael = new Student("Rafael"); $vinicius = new Student("Vinicius"); // Include observers in the math Subject $subject->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 <br />"; echo "<pre>"; print_r($subject->GET_log()); echo "</pre>"; echo "<hr>"; echo "Second Subject <br />"; echo "<pre>"; print_r($subject2->GET_log()); echo "</pre>"; ?> 

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 

)

亦可參考


观察者模式, 此條目需要补充更多来源, 2013年3月21日, 请协助補充多方面可靠来源以改善这篇条目, 无法查证的内容可能會因為异议提出而被移除, 致使用者, 请搜索一下条目的标题, 来源搜索, 网页, 新闻, 书籍, 学术, 图像, 以检查网络上是否存在该主题的更多可靠来源, 判定指引, 觀察者模式是軟體設計模式的一種, 在此種模式中, 一個目標物件管理所有相依於它的觀察者物件, 並且在它本身的狀態改變時主動發出通知, 這通常透過呼叫各觀察者所提供的方法來實現, 此種模式通常被用在即時事件處理系統, 目录, 結. 此條目需要补充更多来源 2013年3月21日 请协助補充多方面可靠来源以改善这篇条目 无法查证的内容可能會因為异议提出而被移除 致使用者 请搜索一下条目的标题 来源搜索 观察者模式 网页 新闻 书籍 学术 图像 以检查网络上是否存在该主题的更多可靠来源 判定指引 觀察者模式是軟體設計模式的一種 在此種模式中 一個目標物件管理所有相依於它的觀察者物件 並且在它本身的狀態改變時主動發出通知 這通常透過呼叫各觀察者所提供的方法來實現 此種模式通常被用在即時事件處理系統 目录 1 結構 2 參與類別 2 1 抽象目標類別 2 2 目標類別 2 3 抽象觀察者介面 2 4 觀察者類別 3 用途 4 示例 4 1 Python 4 2 C 4 3 C 4 4 PHP結構 编辑 nbsp 參與類別 编辑參與本模式的各類別列出如下 成員函式以模擬的方式列出 抽象目標類別 编辑 此抽象類別提供一個界面讓觀察者進行添附與解附作業 此類別內有個不公開的觀察者串鍊 並透過下列函式 方法 進行作業 添附 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 lt listener A gt subject listenerB Listener lt listener B gt subject simulated event subject notify listeners lt event 1 gt outputs lt listener A gt received event lt event 1 gt lt listener B gt received event lt event 1 gt action subject getUserAction subject notify listeners action Enter something to do hello outputs lt listener A gt received event hello lt listener B gt received event hello C 编辑 C 和其他使用 NET Framework的語言一般無需使用接口和類實現典型的觀察者模式 但是這裡依然給一個例子 using System using System Collections namespace Wikipedia Patterns Strategy IObserver gt 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 gt Implements the IObserver public class Observer1 IObserver public void Update string message Console WriteLine Observer1 message Observer2 gt 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 lt list gt include lt vector gt include lt algorithm gt include lt iostream gt 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 lt ObserverBoardInterface gt iterator pos m obs begin while pos m obs end ObserverBoardInterface pos gt update m humidity m temperature m pressure dynamic cast lt DisplayBoardInterface gt pos gt show pos private float m humidity float m temperature float m pressure list lt ObserverBoardInterface gt m obs A Concrete Observer class CurrentConditionBoard public ObserverBoardInterface public DisplayBoardInterface public CurrentConditionBoard WeatherDataInterface amp a m data a m data registerob this void show cout lt lt CurrentConditionBoard lt lt endl cout lt lt humidity lt lt m h lt lt endl cout lt lt temperature lt lt m t lt lt endl cout lt lt pressure lt lt m p lt lt endl cout lt lt lt lt endl void update float h float t float p m h h m t t m p p private float m h float m t float m p WeatherDataInterface amp m data A Concrete Observer class StatisticBoard public ObserverBoardInterface public DisplayBoardInterface public StatisticBoard WeatherDataInterface amp a m maxt 1000 m mint 1000 m avet 0 m count 0 m data a m data registerob this void show cout lt lt StatisticBoard lt lt endl cout lt lt lowest temperature lt lt m mint lt lt endl cout lt lt highest temperature lt lt m maxt lt lt endl cout lt lt average temperature lt lt m avet lt lt endl cout lt lt lt lt endl void update float h float t float p m count if t gt m maxt m maxt t if t lt m mint m mint t m avet m avet m count 1 t m count private float m maxt float m mint float m avet int m count WeatherDataInterface amp m data int main int argc char argv ParaWeatherData wdata new ParaWeatherData CurrentConditionBoard currentB new CurrentConditionBoard wdata StatisticBoard statisticB new StatisticBoard wdata wdata gt SensorDataChange 10 2 28 2 1001 wdata gt SensorDataChange 12 30 12 1003 wdata gt SensorDataChange 10 2 26 806 wdata gt SensorDataChange 10 3 35 9 900 wdata gt removeob currentB wdata gt SensorDataChange 100 40 1900 delete statisticB delete currentB delete wdata return 0 PHP 编辑 class STUDENT lt php class Student implements SplObserver protected tipo Student private nome private endereco private telefone private email private classes array public function GET tipo return this gt tipo public function GET nome return this gt nome public function GET email return this gt email public function GET telefone return this gt nome function construct nome this gt nome nome public function update SplSubject object object gt SET log Comes from this gt nome I m a student of object gt GET materia gt class TEACHER lt php class Teacher implements SplObserver protected tipo Teacher private nome private endereco private telefone private email private classes array public function GET tipo return this gt tipo public function GET nome return this gt nome public function GET email return this gt email public function GET telefone return this gt nome function construct nome this gt nome nome public function update SplSubject object object gt SET log Comes from this gt nome I teach in object gt GET materia gt Class SUBJECT lt php class Subject implements SplSubject private nome materia private observadores array private log array public function GET materia return this gt nome materia function SET log valor this gt log valor function GET log return this gt log function construct nome this gt nome materia nome this gt log Subject nome was included Adiciona um observador public function attach SplObserver classes this gt classes classes this gt log The classes gt GET tipo classes gt GET nome was included Remove um observador public function detach SplObserver classes foreach this gt classes as key gt obj if obj classes unset this gt classes key this gt log The classes gt GET tipo classes gt GET nome was removed Notifica os observadores public function notify foreach this gt classes as classes classes gt update this gt Application lt php require once teacher class php require once student class php require once subject class php subject new Subject Math marcus new Teacher Marcus Brasizza rafael new Student Rafael vinicius new Student Vinicius Include observers in the math Subject subject gt attach rafael subject gt attach vinicius subject gt 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 gt attach renato subject2 gt attach vinicius subject2 gt attach fabio subject2 gt attach tiago Remove the instance Rafael from subject subject gt detach rafael Notify both subjects subject gt notify subject2 gt notify echo First Subject lt br gt echo lt pre gt print r subject gt GET log echo lt pre gt echo lt hr gt echo Second Subject lt br gt echo lt pre gt print r subject2 gt GET log echo lt pre gt gt OUTPUTFirst SubjectArray 0 gt Subject Math was included 1 gt The Student Rafael was included 2 gt The Student Vinicius was included 3 gt The Teacher Marcus Brasizza was included 4 gt The Student Rafael was removed 5 gt Comes from Vinicius I m a student of Math 6 gt Comes from Marcus Brasizza I teach in Math Second SubjectArray 0 gt Subject English was included 1 gt The Teacher Renato was included 2 gt The Student Vinicius was included 3 gt The Student Fabio was included 4 gt The Student tiago was included 5 gt Comes from Renato I teach in English 6 gt Comes from Vinicius I m a student of English 7 gt Comes from Fabio I m a student of English 8 gt 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 取自 https zh wikipedia org w index php title 观察者模式 amp oldid 73628310, 维基百科,wiki,书籍,书籍,图书馆,

文章

,阅读,下载,免费,免费下载,mp3,视频,mp4,3gp, jpg,jpeg,gif,png,图片,音乐,歌曲,电影,书籍,游戏,游戏。