设计模式实践--观察者模式
介绍
观察者模式,有时又被称为发布(publish )-订阅(Subscribe)模式。是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
典型的观察者模式中会有两个角色,观察者和被观察者
被观察者
被观察对象发生了某种变化时,会从存有自己观察者的数组中得到所有注册过的观察者,然后调用观察者的指定方法,将变化通知观察者。
<?php
class Paper
{
private $observers = [];
/* 注册观察者 */
public function register($sub)
{
$this->observers[] = $sub;
}
/* 外部统一访问 */
public function trigger()
{
if(! empty($this->observers)) {
foreach($this->observers as $observer) {
$observer->update(); //通知变化给观察者
}
}
}
}
观察者
(Observer)将自己注册到被观察对象(Subject)中,被观察对象将观察者保存在自己类中的一个数组中。
<?php
/**
* 观察者要实现的接口
*/
interface Observerable
{
public function update();
}
<?php
class Subscriber implements Observerable
{
public function update(){
echo "do something";
}
}
然后在某个地方将观察者注册到被观察者上,需要的时候调用观察者的trigger()方法,通知观察者
<?php
$paper = new Paper();
$paper->register(new Subscriber());
$paper->trigger();
框架中的应用
Event
<?php
namespace App\Events;
class Event
{
public $author;
public function __construct()
{
$this->author = 'zhaohehe';
}
}
在框架中,被观察者叫做Event,即事件。然后event本身不再保存自己的观察者,而是将观察者和被观察者之间的绑定放在了EventServiceProvider中。
EventServiceProvider
<?php
namespace App\Providers;
use System\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
'App\Events\Event' => [
'App\Listeners\Listener',
]
];
public function boot()
{
parent::boot();
}
}
上面的EventServiceProvider继承自System\Support\Providers\EventServiceProvider,父类的代码如下:
<?php
namespace System\Support\Providers;
use System\Events\Dispatcher;
use System\Support\ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [];
public function boot()
{
$Event = $this->app->make('events');
foreach ($this->listens() as $event => $listeners) {
foreach ($listeners as $listener) {
$Event->listen($event, $listener);
}
}
}
public function register()
{
}
public function listens()
{
return $this->listen;
}
}
System\Support\Providers\EventServiceProvider父类用$listen属性来从子类那里拿到保存有event和listener之间关系的数组。(其实这也算是一个微小的常用设计模式:在父类中对某一个变量进行操作,然后由子类来定义这个变量。)然后父类会通过$this->app->make('events');从容器中拿到一个event实例,再用一个foreach循环通过event实例的listen方法来给每一个event注册一个或多个listener。
Dispatcher
从容器中拿出来的event实例实际上是System\Foundation\Container\Dispatcher
public function listen($event, $listener)
{
$this->listeners[$event][] = $this->makeListener($listener);
unset($this->sorted[$event]);
}
在listen方法中,Dispatcher用一个二维数组$this->listeners来保存每一个事件对应的观察者,它的makeListener()方法会接收一个listener,最终返回的其实是一个匿名函数,当需要通知事件的观察者时,Dispatcher会取出这个匿名函数,并执行通知,具体怎么通知,接着往下看。
激活event
$e = $app->make('events');
$e->fire(new App\Events\Event());
以上的两行代码表示从容器中取得event实例,然后调用它的fire()方法,并传入你想要激活的event对象,前面说过event实例实质上是一个Dispatcher对象,这个时候就需要看Dispatcher类中fire()方法是怎么将事件通知给该事件的listener的。
public function fire($event, $payload = [])
{
...
foreach ($this->getListeners($event) as $listener) {
$response = call_user_func_array($listener, $payload);
if (! is_null($response)) {
array_pop($this->firing);
return $response;
}
...
}
可以看到,第n行代码调用call_user_func_array,执行listener对象的handle方法,并且将绑定的event作为参数传递过去,你可以去源码中看看$listener变量到底是什么,其实它是一个有两个元素数组,第一个元素是listener对象,第二个元素是调用的方法,默认是handle,你也可以在serviceProvider中自定义。
listener
<?php
namespace App\Listeners;
use App\Events\Event;
class Listener
{
public function __construct()
{
}
public function handle(Event $event)
{
var_dump($event->author);
}
}
以上,就完成了一个事件从注册它的观察者,到触发时间,通知观察者的过程。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: