观察者模式
背景
我们来设想一种场景:需要开发一个用户注册功能,要求用户注册成功之后给用户发送短信提醒,最简单的实现方式就是在注册接口中将用户信息插入数据库之后,调用发送短信接口,发送短信。过几天产品又想注册成功之后,不仅发短信还要为用户赠送礼物,你可以继续在注册接口添加赠送礼物。可是随着产品的想法的变化,你这个注册接口就要不断的变化。我们能不能想一个办法将注册成功之后做的这些事情都单独出来,注册成功之后再去通知他们,让他们各自操作。我们就可以用观察者模式实现这个解耦。
概念
观察者模式用于实现对对象进行观察:一旦主体对象状态发生改变,与之关联的观察者对象会收到通知,并进行相应操作。在观察者模式中会有两类对象,一个是主体对象及被观察者,另一个就是观察者。结合上面谈到的背景,注册这个件事就可以作为主体对象,发送短信和赠送礼物可以作为观察者。
代码实现
下面的代码简单的实现了一个观察者模式,这次当产品想要在注册的时候再添加额外的功能你只需要添加一个观察者就好,不需要再去修改注册的逻辑
interface observer
{
public function update();
}
class register
{
private $observers = [];
public function addOberver(Observer $observer)
{
$this->observers[] = $observer;
}
public function notify()
{
foreach($this->observers as $observer) {
$observer->update();
}
}
public function registerSuccess()
{
echo '用户信息存储成功';
$this->notify();
}
}
class sendSms implement observer
{
public function update()
{
echo '发送成功';
}
}
class giveGift implements observer
{
public function update()
{
echo '赠送礼物成功';
}
}
$sendSms = new sendSms();
$giveGift = new giveGift();
$register = new register();
$register->addOberver($sendSms);
$register->addOberver($giveGift);
$register->registerSuccess();
事件
laravel中的事件就是利用了观察者模式,其中事件其实就是观察者模式中的主体(被观察者),监听就是观察者模式中的观察者。
事件的使用(官方文档中有具体的使用,这里只做简单的记录)
1.注册事件和监听器
在EventServiceProvider的listen数组中添加一个键值对。
class EventServiceProvider
{
$protected $listen = [
'App\Event\Register' => [
'App\Listeners\SendSms',
'App\Listeners\GiveGift',
]
]
}
2.生成事件和监听器
在laravel中可以使用php artisan event:generate生成事件类register、监听类SendSms、监听类GiveGift,当然也可以通过复制提供的例子来改(lumen只能复制例子来改了。。)
事件类:其实就只是一个保存与事件相关的信息的数据容器。比如在这个例子中,我们需要保存具体注册成功的用户,方便知道给谁发短信,给谁赠送礼物。
namespace App\Event
class Register{
//这里传入一个user类,是为了方便获取一些用户的信息,比如手机号、名子之类的方便传给监听器
public fucntion __construct(User $user)
{
$this->user = $user;
}
}
监听器:监听器中必须有一个handle方法,就是在这个方法中去执行具体的操作。
namespace App\Listeners
class SendSms{
public fucntion handle(Register $register)
{
$phone = $register->user->phone;
send();
}
}
namespace App\Listeners
class GiveGift{
public fucntion handle(Register $register)
{
$phone = $register->user->phone;
gift();
}
}
3.事件调度
event(new Register);
原理分析(因为最近在看lumen所以在写的时候可能会对比一下)
对于整个事件调度来说,入口就是事件调度event(new Register)。现在我们来看看调度的整个过程。这个全局函数event,其实就是调用使用了app('event')。
事件调度器的注册
laravel中是在application中注册基础服务提供者时注册了app('event') = new Dispatcher;

lumen中是将他保存在数组中当make时才会注册。其中这个$availableBindings变量中保存了event保存的方法。

事件监听器的注册
在App\Providers\EventServiceProvider 中定义的listen属性,会在boot的时候,利用事件调度器将每个listen中的handle方法保存在匿名函数中。下面是源码中挑出的一部分代码。
class Dispatcher
{
public function listen($events, $listener)
{
foreach ((array) $events as $event) {
$this->listeners[$event][] = function ($event, $payload) use ($listener) {
return call_user_func_array(
$this->createClassCallable($listener), $payload
);
};
}
}
protected function createClassCallable($listener)
{
list($class, $method) = [$listener,'handler'];
return [$this->container->make($class), $method];
}
}
事件监听器的触发
我们一般是调用全局函数event实现事件的监听。其中app('events')就是事件调度器Dispatcher
function event($event, $payload = [], $halt = false)
{
return app('events')->fire($event, $payload, $halt);
}
public function fire($event, $payload = [], $halt = false)
{
list($event, $payload) = $this->parseEventAndPayload(
$event, $payload
);
$responses = [];
//这里的getListeners就是上边listen方法存进去的匿名函数
foreach ($this->getListeners($event) as $listener) {
$response = $listener($event, $payload); //执行匿名函数,即执行listen中的handle方法
if ($halt && ! is_null($response)) {
return $response;
}
if ($response === false) {
break;
}
$responses[] = $response;
}
return $halt ? null : $responses;
}
网友评论