什麼是觀察者模式?
觀察者設計模式能夠更便利地建立查看目標對象狀態的對象,並且提供與核心對象非耦合的指定功能性。
該模式非常簡單:一個對象通過添加一個方法(該方法允許另一個對象,即觀察者 註冊自己)使本身變得可觀察。當可觀察的對象更改時,它會將訊息發送到登入的觀察者。這些觀察者使用該資訊執行的操作與可觀察的對象無關。結果是對象可以相互對話,而不必瞭解原因。
UML
該圖詳細說明了一個使用觀察者設計模式的類設計:
下面是對的說明:
1.MyObject是可觀察對象,它包含一個名為observers的觀察者保護數組。公用方法addObserver()接受一個觀察者的執行個體並將其儲存在觀察者數組內。
2.doSomething()公用方法會被調用,這個方法對MyObject應用狀態變化。隨後,notify()公用方法會被調用,這個方法可遍曆迴圈觀察者數組。
3.MyObjectObserver具有一個名為change()的公用方法,該方法接受MyObject的一個執行個體。這個特定的觀察者接下來會對MyObject的內容執行某些操作。當在觀察者數組中找到特定的觀察者時,MyObject的notify()方法會直接調用change()方法。
使用執行個體:
<?php interface Observable{ function attach( Observer $observer ); function detach( Observer $observer ); function notify(); } class login implements Observable{ const LOGIN_USER_UNKNOW = 1; const LOGIN_WRONG_PASS = 2; const LOGIN_ACCESS = 3; private $status = array(); private $observers = array(); public function setStatus( $status, $user, $ip ) { $this->status = array( $status, $user, $ip ); } public function getStatus() { return $this->status; } public function handleLogin( $user, $pass, $ip ) { switch ( mt_rand( 1, 3 ) ) { case 1: $this->setStatus( self::LOGIN_USER_UNKNOW, $user, $ip ); $ret = false; break; case 2: $this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip ); $ret = false; break; case 3: $this->setStatus( self::LOGIN_ACCESS, $user, $ip ); $ret = true; break; } $this->notify(); return $ret; } public function attach( Observer $observer ) { $this->observers[] = $observer; } public function detach( Observer $observer ) { $newObservers = array(); foreach ( $this->observers as $obs ) { if ( $obs !== $observer ) $newObservers[] = $obs; } $this->observers = $newObservers; } public function notify() { foreach ( $this->observers as $obs ) { $obs->update( $this ); } } } interface Observer{ function update( Observable $observable ); } class SecurityMonitor implements Observer{ function update( Observable $observable ) { $status = $observable->getStatus(); if($status[0] == Login::LOGIN_WRONG_PASS){ echo __CLASS__.":".$status[1]."於".$status[2]."登入失敗"; } } } $login = new Login(); $login->attach(new SecurityMonitor()); $login->handleLogin('XXX','XXX','127.0.0.1'); ?> 出錯時的運行結果: SecurityMonitor:XXX於127.0.0.1登入失敗[Finished in 0.1s]
代碼中可以看到login對象主動添加SecurityMonitor對象觀察。這樣要調用Login::getStatus(),SecurityMonitor類就必須瞭解更多資訊。雖然調用發生於一個ObServable對象上,但無法保證對象也是一個Login對象。為解決這個問題,有一個辦法:斷續保持ObServable介面的通用性,由ObServer類負責保證它們的主體是正確的類型。它們甚至能將自己添加到主體上。