了解观察者模式

Recently I was asked to integrate third-party forum software into an existing web portal. The problem was, both applications had their own separate authentication mechanism. From a user perspective it would be ideal for users to log in to the portal without having to log in to the forum separately. And in this case, I didn’t want to modify the forum code unnecessarily since it would create a maintenance nightmare – I’d have to merge any subsequent updates and bug fixes from the vendor and be careful not to overwrite my own modifications. This is where the Observer Pattern came in handy for me.

最近,有人要求我将第三方论坛软件集成到现有的Web门户中。 问题是,两个应用程序都有各自独立的身份验证机制。 从用户的角度来看,用户登录到门户而不必单独登录论坛是理想的。 在这种情况下,我不想不必要地修改论坛代码,因为这会造成维护方面的噩梦–我必须合并供应商的所有后续更新和错误修复,并注意不要覆盖自己的修改。 这就是观察者模式对我派上用场的地方。

In this article I’ll show you how to implement the Observer Pattern. You’ll learn how various classes in the pattern relate to one another as subject and observers, how the subject notifies observers of a change in its state, and how to identify scenarios where it would be suitable to use the Observer Pattern in your own code.

在本文中,我将向您展示如何实现观察者模式。 您将学习模式中的各个类如何作为主题和观察者相互关联,主题如何通知观察者其状态发生变化,以及如何确定适合在自己的代码中使用观察者模式的场景。

观察者模式 (The Observer Pattern)

The Observer Pattern (also known as Publish-Subscribe Pattern) is a behavioral design pattern which defines a one-to-many relationship between objects such that, when one object changes its state, all dependent objects are notified and updated automatically. In my case, I used this pattern to link the portal’s authentication mechanism with the forum software. The act of logging into the portal also automatically triggered the user to be logged into the forum as well.

观察者模式(也称为发布-订阅模式)是一种行为设计模式,它定义了对象之间的一对多关系,这样,当一个对象更改其状态时,所有相关对象都将得到自动通知和更新。 就我而言,我使用这种模式将门户网站的身份验证机制与论坛软件链接在一起。 登录门户网站的行为也自动触发用户也登录到论坛。

An object with a one-to-many relationship with other objects who are interested in its state is called the subject or publisher. Its dependent objects are called observers or subscribers. The observers are notified whenever the state of the subject changes and can act accordingly. The subject can have any number of dependent observers which it notifies, and any number of observers can subscribe to the subject to receive such notifications.

与其他对其状态感兴趣的对象具有一对多关系的对象称为主题发布者 。 它的从属对象称为观察者订户 。 只要主题的状态发生变化,观察者就会收到通知,并可以采取相应的行动。 主题可以有任意数量的从属观察者通知,并且任何数量的观察者可以订阅该主题以接收此类通知。

观察者类 (The Observer Class)

The Observer class provides an update() method which will be called by the subject to notify it of the subject’s state change. In this example, I’ve defined the update() method as a concrete method. If you like, you can define the method here as abstract and then provide a concrete implementation for it in your observer’s subclass.

Observer类提供了一个update()方法,主题将调用此方法,以将主题的状态更改通知给它。 在此示例中,我将update()方法定义为具体方法。 如果愿意,可以在此处将方法定义为abstract ,然后在观察者的子类中为其提供具体的实现。

<?php
abstract class Observer
{
    public function __construct($subject = null) {
        if (is_object($subject) && $subject instanceof Subject) {
            $subject->attach($this);
        }
    }

    public function update($subject) {
        // looks for an observer method with the state name
        if (method_exists($this, $subject->getState())) {
            call_user_func_array(array($this, $subject->getState()), array($subject));
        }
    }
}

The __construct() method accepts an instance of an observable subject and attaches itself to the subject – which I’ll get to momentarily. The update() method retrieves the subject’s current state and uses it to call a subclassed observer method with the same name as the state.

__construct()方法接受一个可观察主题的实例,并将其自身附加到该主题上,我将在稍后进行介绍。 update()方法检索主题的当前状态,并使用它来调用与该状态同名的子类观察器方法。

So, in my particular case, I needed to make the portal’s existing Auth class an observable subject and create a concrete observer subclass to hook into the forum’s authentication code. My subclass also needed to implement methods using the subject’s states.

因此,在我的特殊情况下,我需要使门户网站的现有Auth类成为可观察的主题,并创建一个具体的观察者子类以加入论坛的身份验证代码。 我的子类还需要使用主体的状态来实现方法。

学科课 (The Subject Class)

The Subject class is also an abstract class and defines four primary methods: attach(), detach(), setState(), and notify(). For convenience, I’ve also added getState() and getObservers() methods here.

Subject类也是一个抽象类,它定义了四个主要方法: attach()detach()setState()notify() 。 为了方便起见,我还在此处添加了getState()getObservers()方法。

<?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;
    }
}

The attach() method subscribes an observer to the subject so that any changes in state can be conveyed to it. The detach() method unsubscribes the observer from the subject, so that it no longer observes the subject for state changes.

attach()方法使观察者订阅主题,以便可以将状态的任何变化传达给主题。 detach()方法使观察者退订主题,因此它不再观察主题的状态变化。

The setState() method sets the subject’s current state and calls notify() to update the observers, i.e. publishes notifications to each observer. The notify() method in turn updates each of the subscribed objects by iterating through the internal list and calling each member’s update() method.

setState()方法设置主题的当前状态,并调用notify()来更新观察者,即向每个观察者发布通知。 notify()方法依次遍历内部列表并调用每个成员的update()方法,从而更新每个订阅的对象。

The getState() and getObservers() methods are simply helper functions to return the current subject’s state and list of observers.

getState()getObservers()方法只是返回当前主题的状态和观察者列表的辅助函数。

仔细观察…将其放在一起 (Observe Closely… Bringing it Together)

Now with abstract base classes for both an observer and subject, I was able to integrate the forum software into the existing web portal. I needed to make the portal’s Auth class an observable subject and set its observable state when the user logs in or out of the portal.

现在有了针对观察者和主题的抽象基类,我能够将论坛软件集成到现有的Web门户中。 我需要使门户网站的Auth类成为可观察的主题,并在用户登录或注销门户时设置其可观察状态。

<?php
class Auth extends Subject
{
    function login() {
        // existing code to perform login authentication
        // ...

        // signal any observers that the user has logged in
        $this->setState("login");
    }

    function logout() {
        // existing code to perform some operation when user logs out
        // e.g. destroy session, etc...

        // signal any observers that the user has logged out
        $this->setState("logout");
    }
}

I extended the Subject class so that Auth is observable, and then added a call to setState() in both the login() and logout() methods.

我扩展了Subject类,以便可以观察到Auth ,然后在login()logout()方法中都添加了对setState()的调用。

To subclass the Observer, I created an Auth_ForumHook class which was responsible for calling the forum’s API login and logout functions.

为了对Observer子类化,我创建了Auth_ForumHook类,该类负责调用论坛的API登录和注销功能。

<?php
class Auth_ForumHook extends Observer
{
    function login($subject) {
        // call the forum's API functions to log the user in
        // ...
    }

    function logout($subject) {
        // call the forum's API functions to log the user out
        // ...
    }
}

Elsewhere in the code base, where the portal’s Auth class was being instantiated, I attached an Auth_ForumHook, instance to it so that the observer would be notified of any state changes in Auth.

在代码库的其他地方(实例化门户网站的Auth类),我Auth_ForumHook,附加了一个Auth_ForumHook,实例,以便可以向观察者通知Auth的任何状态更改。

<?php
$auth = new Auth();
// attach an observer
$auth->attach(new Auth_ForumHook());
...

That was the extent of my extra coding requirements – apart from preparing the abstract Observer and Subject classes. Any change in state triggered by Auth‘s login() and logout() methods notified the Auth_ForumHook observer and automatically logged the user in or out of the forum.

那是我额外的编码要求的范围–除了准备抽象的ObserverSubject类。 Authlogin()logout()方法触发的任何状态更改都会通知Auth_ForumHook观察者,并自动将用户登录到论坛或从论坛注销。

To add new observers, say, a login tracker to record when a user logs in or out of the portal, one would just have to provide a concrete Observer class and attach it to the Auth subject without the need to further modify the existing Auth object’s login() and logout() methods.

要添加新的观察者(例如登录跟踪器)以记录用户何时登录或退出门户,只需提供一个具体的Observer类并将其附加到Auth主题,而无需进一步修改现有的Auth对象的login()logout()方法。

摘要 (Summary)

The Observer Pattern is an appropriate design pattern to apply in any situation where you have several objects which are dependent on another object and are required to perform an action when the state of that object changes, or an object needs to notify others without knowing who they are or how many there are.

观察者模式是一种适当的设计模式,适用于以下情况:具有多个对象的对象,这些对象依赖于另一个对象,并且在该对象的状态发生变化时需要执行某个操作,或者某个对象需要通知其他人而又不知道他们是谁是或有多少。

In this article, I have shown you the basic Subject-Observer pattern and provided concrete examples of how you can use this behavioral pattern to easily extend the functionality of an existing class without tightly coupling any new requirements. This pattern enables you to achieve a higher level of consistency between related and dependent objects without sacrificing code re-usability.

在本文中,我向您展示了基本的Subject-Observer模式,并提供了一些具体示例,说明了如何使用此行为模式轻松扩展现有类的功能而无需紧密耦合任何新要求。 这种模式使您可以在不牺牲代码可重用性的情况下,在相关对象和从属对象之间实现更高级别的一致性。

Image via JPF / Shutterstock

图片来自JPF / Shutterstock

翻译自: https://www.sitepoint.com/understanding-the-observer-pattern/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值