Day27. 范例:Line群组通知(观察者模式)

本文同步更新於blog

情境:让我们用Line群组,来实作观察者模式

  • 首先实作抽象的观察者类别 (Observer)

其中会有接收到主题类别通知时的更新方法 (具体实作内容由子类决定)。

<?php

namespace App\ObserverPattern\LineGroup;

abstract class Observer
{
    abstract public function update();
}
  • 接着实作抽象的主题类别 (Subject)

它会有新增观察者移除观察者通知观察者的方法。

<?php

namespace App\ObserverPattern\LineGroup;

use App\ObserverPattern\LineGroup\Observer;

abstract class Subject
{
    /**
     * @var array
     */
    protected $observers = [];

    public function attachObserver(Observer $observer)
    {
        $this->observers[] = $observer;
    }

    public function detachObserver(Observer $observer)
    {
        $index = array_search($observer, $this->observers);

        if ($index >= 0) {
            unset($this->observers[$index]);
        }
    }

    public function notifyObservers()
    {
        foreach ($this->observers as $observer) {
            $observer->update();
        }
    }
}

  • 利用刚刚建立的观察者类别,实作使用者
<?php

namespace App\ObserverPattern\LineGroup;

use App\ObserverPattern\LineGroup\Observer;

class User extends Observer
{
    protected $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function update()
    {
        //使用者手机跳出通知
        //使用者电脑跳出通知
    }
}

  • 实作群组聊天室,并模拟使用情况
<?php

namespace App\ObserverPattern\LineGroup;

use App\ObserverPattern\LineGroup\Subject;
use App\ObserverPattern\LineGroup\User;

class Program extends Subject
{
    protected $state = 'nothing new';

    public function run()
    {
        //Bear加入群组
        $bear = new User('Bear');
        $this->attachObserver($bear);

        //通知群组
        $this->notifyObservers();

        //Jane加入群组
        $jane = new User('Jane');
        $this->attachObserver($jane);

        //通知群组
        $this->notifyObservers();

        //有新讯息,通知群组
        $this->state = 'new message';
        $this->notifyObservers();
        $this->state = 'nothing new';

        //Jane离开群组
        $this->detachObserver($jane);

        //通知群组
        $this->notifyObservers();
    }
}


[单一职责原则]
我们将主题类别 (Subject)观察者类别 (Observer) 视作两种不同的职责。
目前范例尚未将聊天室的职责通知的职责分离。

[开放封闭原则]
当新增新的主题类别时,我们可以轻易地新增、移除、通知对应观察者。
亦可以新增新的观察者类别,再将其加入既有的主题类别。

[里氏替换原则]
透过子类来定义观察者类别中更新方法具体的实作。
目前范例的缺点是,观察者类别不好对更新方法取更适合的命名

[依赖反转原则]
抽象主题类别依赖於抽象的观察者类别。
使用者类别实作抽象的观察者类别。

主题类别不必知道具体观察者想做什麽,
仅需知道观察者类别有接口可以通知更新。


最後附上类别图:
https://ithelp.ithome.com.tw/upload/images/20201012/20111630Q892DiTT4B.png
(注:若不熟悉 UML 类别图,可参考UML类别图说明。)

ʕ •ᴥ•ʔ:架构容易,细节仍需琢磨的模式。


<<:  Day27-移动侦测1

>>:  [Day28]简单的contextmenu

[Day 7] 实作 Request Data Validation 及 Global Exception Handler

昨天提到如何使用 kotlinx.serialization 处理 request/response...

Day25阵列(JavaScript)

Array阵列 简单来说 阵列就是一个有序的序列而且里面可以储存不定数量的任何值 我是把它想像成一个...

[ Day 23 ] - 阵列资料处理 - map

阵列资料处理 - map 特性 可以将原始阵列经过处理後,重新组合回传一个新阵列 不会影响原始阵列 ...

Day06-CRUD API 实作(六)CRUD 实作(下)

大家好~ 今天要来完成我们留言的读取、更新与删除功能罗。 Controller Read 查询全部留...

Day09 - 在 Next.js 中使用 pre-rendering (getServerSideProps)

前言 Next.js 的 pre-rendering 分成两种形式,一种是 SSG (Static ...