iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 9
1
Software Development

PHP 大師之路 - 開源的技術淬練系列 第 9

Day 9 - PHP 設計模式:觀察者 (Observer)

  • 分享至 

  • xImage
  •  

在介紹 PHP 的觀察者模式 (observer pattern) 寫法之前,先借用 JavaScript 的範例來說明,因為 JavaScript 已經內建此模式,且 JavaScript 是更被廣泛使用的程式語言,筆者相信會閱讀本篇的讀者們可能都接觸過,因此以此舉例幫助理解。

範例:

JavaScript

var button = document.getElementById('btn');

buttonElement.addEventListener('click', function (event) {
    alert('按紐被點擊了。');
});

jQuery 版本

$('#btn').click(function() {
    alert('按紐被點擊了。');
});

一個函式被註冊進在一個 DOM 元素 id 為 btnclick 事件,當該元素被點擊時,觸發 click 事件,執行函式。結果為跳出一個視窗提示按紐被點擊了

在 JavaScript 中,滑鼠游標及鍵盤在瀏覽器頁面上所能觸發的這些 DOM 事件,雖然在 PHP 中並沒有這種人機互動事件,但我們可以在 PHP 應用程式中實作觀察者模式,自定我們在應用程式中實際需要的事件及觸發事件執行的機制。

出版/訂閱 (Publish/Subscribe)

這是比較白話的解釋。

觀察者模式就像報社的行為一樣,民眾們向報社訂閱報紙,當報社出版報紙時,就會送一份給在訂閱名單內的人。如果你不想訂了,取消訂閱後,已不在訂閱名單中,報社出版報紙後,就不再送一份給你。

因此觀察者模式就有另一個名字,名為「出版/訂閱」模式。

生活面的圖例

出版/訂閱模式

  • 如圖中,胖虎、小夫及大雄先前已向西瓜日報出版社訂閱了報紙,因此在訂閱者名單中。
  • 當西瓜日報出版後,會向在訂閱者名單中的每一個人投送一份報紙。
  • 靜香和多啦A夢因為不在訂閱者名單中,所以西瓜日報並不會投送給他們出版的報紙。

程式面的圖例

觀察者模式

用觀察者模式的名詞之後,對比訂閱報紙的圖例,可以發現是一樣的概念。

用 JavaScript 比喻的圖例

JavaScript 觀察者模式

這就是一開始使用 JavaScript 的範例。一個函式註冊到 DOM id 為 btn 的 click 事件中,當該事件發生狀態變更時,通知註冊的觀察者,此時觀察者便進行自己要做的事。

PHP 觀察者模式實作

這個範例程式碼 Event 類別實作了三個方法,addListener 用來註冊回呼函式。trigger 用來觸發回呼函式。has 檢查指定的名稱事件是否已存在。

範例:/day-9/example-1/Event.php

class Event
{
    /**
     * 儲放 callback 的陣列
     *
     * @var array
     */
    protected static $event = [];

    /**
     * 註冊事件
     *
     * @param string $name     事件名稱
     * @param mixed  $callback 可執行的函式或閉包
     *
     * @return void
     */
    public static function addListener($name, $callback)
    {
        $name = strtolower($name);
        self::$event[$name][] = $callback;
    }

    /**
     * 觸發事件
     *
     * @param string $name 事件名稱
     * @param mixed  $args 傳給回呼函式的參數
     *
     * @return void
     */
    public static function trigger($name, $args)
    {
        $name = strtolower($name);

        if (self::has($name)) {
            foreach (self::$event[$name] as $func) {
                $func($args);
            }
        }
    }

    /**
     * 檢查事件是否存在
     *
     * @param string $name 事件名稱
     *
     * @return bool
     */
    public static function has($name) 
    {
        if (isset(self::$event[$name])) {
            return true;
        }
        
        return false;
    }
}

範例:/day-9/example-1/Example.php

執行:

include 'Event.php';

Event::addListener('day9_event', function($args) {
    if ($args) {
        echo 'Hello, ' . $args . '.';
    } else {
        echo 'Hello, world.';
    }
});

Event::trigger('day9_event', 'Taiwan');

我們註冊了 day9_event 這個事件,並傳入一個閉包函式。觸發 day9_event 事件時傳入參數字串 Taiwan

結果:

Hello, Taiwan.

現有套件

Symfony 的元件家族中有一個觀察者模式元件,Event Dispatcher,可直接透過 Composer 安裝使用:

composer require symfony/event-dispatcher

其功能及文件範例完整。但如果讀者們覺得太複雜,只想要可以立則上手、簡單地使用,可以使用筆者寫的套件

composer require shieldon/event-dispatcher

結論

觀察者模式非常常用,筆者特別介紹這個設計模式,主要是為了接下來的文章鋪路。

筆者要帶大家設計的 WordPress 外掛,便會提到 WordPress 的 Hook,它就是觀察者模式,只是不同名稱的呈現。而 Hook 是 WordPress 整個架構功能易於讓外掛程式改寫、擴充的關鍵。

在前幾篇文章筆者提過,設計模式是一種最佳實踐的程式設計習慣,但每個人的習慣不一樣,至少在類別及方法命名常常會有些許的不一樣,因此 WordPress 的命名不一樣,如果了解了,很快就能上手了。


本篇原始碼可在此瀏覽
本文同步更新於 TerryL 部落格 Day 9 - PHP 設計模式:觀察者 (Observer),歡迎前往討論。


上一篇
Day 8 - PHP 設計模式:依賴注入 (Dependency Injection)
下一篇
Day 10 - PHP 建議規範 (PSR)
系列文
PHP 大師之路 - 開源的技術淬練30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言