核心要点
update()
方法,主题调用该方法以通知其状态变化。主题类定义主要方法:attach()
、detach()
、setState()
和notify()
,用于管理观察者并通知他们状态变化。我最近被要求将第三方论坛软件集成到现有的 Web 门户中。问题是,这两个应用程序都有各自独立的身份验证机制。从用户的角度来看,理想情况下,用户无需单独登录论坛即可登录门户。在这种情况下,我不希望不必要地修改论坛代码,因为这会造成维护噩梦——我必须合并供应商的任何后续更新和错误修复,并小心避免覆盖我自己的修改。这就是观察者模式对我来说非常方便的地方。在本文中,我将向您展示如何实现观察者模式。您将学习模式中各种类如何相互关联作为主题和观察者,主题如何通知观察者其状态的变化,以及如何在您自己的代码中识别适合使用观察者模式的场景。
观察者模式
观察者模式(也称为发布-订阅模式)是一种行为型设计模式,它定义对象之间的一对多关系,以便当一个对象改变其状态时,所有依赖对象都会自动收到通知并更新。在我的例子中,我使用此模式将门户的身份验证机制与论坛软件链接。登录门户的行为也会自动触发用户登录论坛。与其他对自身状态感兴趣的对象具有一对多关系的对象称为主题或发布者。其依赖对象称为观察者或订阅者。每当主题的状态发生变化时,观察者都会收到通知,并可以相应地采取行动。主题可以拥有任意数量的依赖观察者,它会向这些观察者发出通知,并且任意数量的观察者都可以订阅主题以接收此类通知。
观察者类
观察者类提供一个update()
方法,主题将调用该方法以通知其状态变化。在此示例中,我已将update()
方法定义为具体方法。如果您愿意,您可以在此处将该方法定义为抽象方法,然后在观察者的子类中为其提供具体实现。
<code class="language-php"><?php abstract class Observer { public function __construct($subject = null) { if (is_object($subject) && $subject instanceof Subject) { $subject->attach($this); } } public function update($subject) { // 查找具有状态名称的观察者方法 if (method_exists($this, $subject->getState())) { call_user_func_array(array($this, $subject->getState()), array($subject)); } } }</code>
__construct()
方法接受可观察主题的实例,并将自身附加到主题——我稍后会讲到。update()
方法检索主题的当前状态,并使用它来调用与状态名称相同的子类化观察者方法。因此,在我的特定情况下,我需要将门户现有的 Auth 类设为可观察主题,并创建一个具体的观察者子类来连接到论坛的身份验证代码。我的子类还需要使用主题的状态来实现方法。
主题类
主题类也是一个抽象类,它定义了四个主要方法:attach()
、detach()
、setState()
和notify()
。为方便起见,我还在此处添加了getState()
和getObservers()
方法。
<code class="language-php"><?php abstract class Subject { protected $observers; protected $state; public function __construct() { $this->observers = array(); $this->state = null; } public function attach(Observer $observer) { $i = array_search($observer, $this->observers); if ($i === false) { $this->observers[] = $observer; } } public function detach(Observer $observer) { if (!empty($this->observers)) { $i = array_search($observer, $this->observers); if ($i !== false) { unset($this->observers[$i]); } } } public function getState() { return $this->state; } public function setState($state) { $this->state = $state; $this->notify(); } public function notify() { if (!empty($this->observers)) { foreach ($this->observers as $observer) { $observer->update($this); } } } public function getObservers() { return $this->observers; } }</code>
attach()
方法将观察者订阅到主题,以便可以向其传达任何状态更改。detach()
方法将观察者从主题中取消订阅,以便它不再观察主题的状态更改。setState()
方法设置主题的当前状态并调用notify()
来更新观察者,即向每个观察者发布通知。notify()
方法依次通过迭代内部列表并调用每个成员的update()
方法来更新每个已订阅的对象。getState()
和getObservers()
方法只是返回当前主题的状态和观察者列表的辅助函数。
仔细观察……整合在一起
现在有了观察者和主题的抽象基类,我能够将论坛软件集成到现有的 Web 门户中。我需要将门户的 Auth 类设为可观察主题,并在用户登录或注销门户时设置其可观察状态。
<code class="language-php"><?php class Auth extends Subject { function login() { // 执行登录身份验证的现有代码 // ... // 向任何观察者发出信号,表明用户已登录 $this->setState("login"); } function logout() { // 执行用户注销时执行某些操作的现有代码 // 例如销毁会话等... // 向任何观察者发出信号,表明用户已注销 $this->setState("logout"); } }</code>
我扩展了 Subject 类,以便 Auth 可观察,然后在 login()
和 logout()
方法中添加了对 setState()
的调用。为了对观察者进行子类化,我创建了一个 Auth_ForumHook 类,该类负责调用论坛的 API 登录和注销函数。
<code class="language-php"><?php class Auth_ForumHook extends Observer { function login($subject) { // 调用论坛的 API 函数以登录用户 // ... } function logout($subject) { // 调用论坛的 API 函数以注销用户 // ... } }</code>
在代码库的其他地方,在实例化门户的 Auth 类的地方,我将 Auth_ForumHook 实例附加到它,以便观察者会收到 Auth 中任何状态更改的通知。
<code class="language-php"><?php abstract class Observer { public function __construct($subject = null) { if (is_object($subject) && $subject instanceof Subject) { $subject->attach($this); } } public function update($subject) { // 查找具有状态名称的观察者方法 if (method_exists($this, $subject->getState())) { call_user_func_array(array($this, $subject->getState()), array($subject)); } } }</code>
除了准备抽象的 Observer 和 Subject 类之外,这就是我的额外编码需求的全部内容。Auth 的 login()
和 logout()
方法触发的任何状态更改都会通知 Auth_ForumHook 观察者,并自动登录或注销论坛中的用户。要添加新的观察者,例如,登录跟踪器以记录用户何时登录或注销门户,只需提供一个具体的 Observer 类并将其附加到 Auth 主题即可,而无需进一步修改现有的 Auth 对象的 login()
和 logout()
方法。
总结
如果您有多个依赖于另一个对象的对象,并且需要在该对象的状态发生变化时执行操作,或者一个对象需要通知其他对象而不知道它们是谁或有多少个,那么观察者模式是一个合适的适用设计模式。在本文中,我向您展示了基本的主题-观察者模式,并提供了具体的示例,说明如何使用这种行为模式轻松扩展现有类的功能,而无需紧密耦合任何新的需求。此模式使您能够在相关和依赖对象之间实现更高水平的一致性,而不会牺牲代码的可重用性。
图片来自 JPF / Shutterstock
(后续的FAQs部分,由于篇幅过长,已省略。核心内容已在上面重新组织和润色。)
以上是了解观察者模式的详细内容。更多信息请关注PHP中文网其他相关文章!