PHP设计模式之观察者模式
观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
实现方式
观察者模式有很多实现方式,从根本上说,该模式必须包含两个角色:观察者和被观察对象。在刚才的例子中,业务数据是被观察对象,用户界面是观察者。观察者和被观察者之间存在“观察”的逻辑关联,当被观察者发生改变的时候,观察者就会观察到这样的变化,并且做出相应的响应。如果在用户界面、业务数据之间使用这样的观察过程,可以确保界面和数据之间划清界限,假定应用程序的需求发生变化,需要修改界面的表现,只需要重新构建一个用户界面,业务数据不需要发生变化。
使用场景
-
当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
-
当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要被改变。
-
当一个对象必须通知其他对象,而它又不能假定其他对象是谁。换言之,不希望这些对象是紧密耦合的。
角色
1. 抽象主题(Subject):
它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
2. 具体主题(Concrete Subject):
将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
3. 抽象观察者(Observer):
为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
4. 具体观察者(Concrete Observer):
实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
优缺点
观察者模式的主要的作用就是对对象解耦,将观察者和被观察者完全隔离。
- 观察者模式的优点
观察者模式解除了主题和具体观察者的耦合让耦合的双方都依赖于抽象,而不是依赖具体。
- 观察者模式的缺点
在应用观察者模式时需要考虑一下开发小路问题,程序中包括一个被观察者和多个被观察者,开发和调试比较复杂,而且Java中的消息的通知默认是顺序执行的,一个观察者的卡顿会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。
示例
<?php
interface Subject
{
public function attach(Observer $observer);
public function dettach(Observer $observer);
public function notify();
}
interface Observer
{
public function update(Subject $subject);
}
class User implements Subject
{
public $login;
public $logout;
protected $observers = [];
public function hash_update($login = false, $logout = false)
{
$this->login = $login;
$this->logout = $logout;
$this->notify();
}
public function attach(Observer $observer)
{
$this->observers[] = $observer;
}
public function dettach(Observer $observer)
{
}
public function notify()
{
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
}
class LoginUpdateSubsrcibe implements Observer
{
public function update(Subject $subject)
{
if ($subject->login) {
echo "你已登陆,xxx... <br/>";
}
}
}
class LogoutUpdateSubsrcibe implements Observer
{
public function update(Subject $subject)
{
if ($subject->logout) {
echo "你已退出,xxx... <br/>";
}
}
}
$user = new User();
$user->attach(new LoginUpdateSubsrcibe());
$user->attach(new LogoutUpdateSubsrcibe());
$user->hash_update(true,true);
?>