在介紹 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 為 btn
的 click
事件,當該元素被點擊時,觸發 click
事件,執行函式。結果為跳出一個視窗提示按紐被點擊了。
在 JavaScript 中,滑鼠游標及鍵盤在瀏覽器頁面上所能觸發的這些 DOM 事件,雖然在 PHP 中並沒有這種人機互動事件,但我們可以在 PHP 應用程式中實作觀察者模式,自定我們在應用程式中實際需要的事件及觸發事件執行的機制。
這是比較白話的解釋。
觀察者模式就像報社的行為一樣,民眾們向報社訂閱報紙,當報社出版報紙時,就會送一份給在訂閱名單內的人。如果你不想訂了,取消訂閱後,已不在訂閱名單中,報社出版報紙後,就不再送一份給你。
因此觀察者模式就有另一個名字,名為「出版/訂閱」模式。
用觀察者模式的名詞之後,對比訂閱報紙的圖例,可以發現是一樣的概念。
這就是一開始使用 JavaScript 的範例。一個函式註冊到 DOM id 為 btn 的 click 事件中,當該事件發生狀態變更時,通知註冊的觀察者,此時觀察者便進行自己要做的事。
這個範例程式碼 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),歡迎前往討論。