资讯专栏INFORMATION COLUMN

「HEAD-FIRST」之观察者模式

Songlcy / 2071人阅读

摘要:这是一个设计模式系列本书所有案例均来自设计模式中文版地址欢迎大家观察者模式定义了对象之间的一对多依赖当一个对象改变状态时它的所有依赖者都将会收到通知并自动更新观察者模式形容图设计谜题有一个气象观察站我们希望建立一个应用有三种布告板用于显示

这是一个设计模式系列,本书所有案例均来自「Head-First设计模式(中文版)」, Github地址, 欢迎大家 watch, star

观察者模式

定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都将会收到通知并自动更新.

观察者模式形容图

设计谜题

有一个气象观察站,我们希望建立一个应用,有三种布告板(用于显示不同的气象数据),当气象站获取到最新的测量数据时,我们希望三种布告板能实时更新.

类图设计

其中 WeatherData用于获取气象站最新测量数据(三个get方法),当数据更新时,会调用onChanged方法(不要管为什么,这是气象站内部逻辑).

代码实现

主题接口

interface Sublect
{
    public function registerObserver(Observer $observer);

    public function removeObserver();

    public function nitifyObservers();
}

主题对象 WeatherData

class WeatherData implements Sublect
{
    protected $observers = [];

    protected $pressure, $temperature, $humidity;

    public function registerObserver(Observer $observer)
    {
        if (array_search($observer, $this->observers) === false) {
            $this->observers[] = $observer;
        }
    }

    public function removeObserver()
    {
        if (($index = array_search($observer, $this->observers)) !== false) {
            unset($this->observers[$index]);
        }
    }

    public function nitifyObservers()
    {
        foreach ($this->observers as $observer) {
            $observer->update($this->getPressure(), $this->getTemperature(), $this->getHumidity());
        }
    }

    public function onChanged()
    {
        $this->nitifyObservers();
    }

    //获取最新气压
    public function getPressure()
    {
        return $this->pressure;
    }

    //获取最新温度
    public function getTemperature()
    {
        return $this->temperature;
    }

    //获取最新湿度
    public function getHumidity()
    {
        return $this->humidity;
    }

    //测试
    public function youNeedChanged()
    {
        $this->pressure = mt_rand(1, 99);
        $this->temperature = mt_rand(1, 99);
        $this->humidity = mt_rand(1, 99);

        $this->onChanged();
    }
}

观察者接口

interface Observer
{
    //气压/温度/湿度
    public function update($pressure, $temperature, $humidity);
}

显示面板接口

interface DisplayElement
{
    public function display();
}

观察者对象集

class CurrentConditionsDisplay implements Observer, DisplayElement
{
    protected $subject;

    protected $pressure, $temperature, $humidity;

    //这里为什么会保留 Subject 接口的引用是为了方便的 remove 及 registe
    public function __construct(Sublect $subject)
    {
        $this->subject = $subject;
        $this->subject->registerObserver($this);
    }

    public function update($pressure, $temperature, $humidity)
    {
        $this->pressure = $pressure;
        $this->temperature = $temperature;
        $this->humidity = $humidity;

        $this->display();
    }

    public function display()
    {
        echo "Current pressure: {$this->pressure}, Current temperature: {$this->temperature}";
    }
}

//其他两种布告板省略

测试

$weatherData = new WeatherData();

$display = new CurrentConditionsDisplay($weatherData);//把当前布告栏注册成为观察者
//$other = new OthersDisplay($weatherData);//把当前布告栏注册成为观察者
//$other = new OtherDisplay($weatherData);//把当前布告栏注册成为观察者

$weatherData->youNeedChanged();//气象站数据更新了会导致布告板实时更新
//Current pressure: 33, Current temperature: 46
另一种形式的观察者模式

我们知道,观察者总是被动的接受主题对象的推送,但有些场景下,我们希望观察者能主动的去获取数据;毕竟观察者数量这么多,主题对象不可能事先知道每个观察者需要的状态,并且也不会导致明明只需要一点点数据,却被迫收到一堆.

我们来重写设计上面的问题.

类图基本保持不变,只是在WeatherData类新增了setChanged方法并改变了Observer接口update签名.

重构后的主题接口

interface Sublect
{
    public function registerObserver(Observer $observer);
    public function removeObserver();
    public function nitifyObservers($args = null);
}

interface Observer
{
    public function update(Sublect $subject, $object = null);
}

重构后的主题对象

class WeatherData implements Sublect
{
    protected $observers = [];

    protected $pressure, $temperature, $humidity, $changed;

    public function nitifyObservers($args = null)
    {
        if ($this->changed) {
            foreach ($this->observers as $observer) {
                $observer->update($this, $args);
            }
            $this->changed = false;
        }
    }

    public function onChanged()
    {
        $this->setChanged();

        $this->nitifyObservers([
            "pressure" => $this->pressure,
            "temperature" => $this->temperature,
            "humidity" => $this->humidity,
        ]);
    }

    public function setChanged()//新增方法
    {
        $this->changed = true;
    }

    //其他方法保持不变
}

重构后的布告板对象

class CurrentConditionsDisplay implements Observer, DisplayElement
{
    protected $subject;

    protected $pressure, $temperature, $humidity;

    //这里为什么会保留 Subject 接口的引用是为了方便的 remove 及 registe
    public function __construct(Sublect $subject)
    {
        $this->subject = $subject;
        $this->subject->registerObserver($this);
    }

    public function update(Sublect $subject, $object = null)
    {
        if ($subject instanceof Sublect) {
            //你可以用 拉取 的形式获取最新数据
            $this->pressure = $subject->getPressure();
            $this->temperature = $subject->getTemperature();
            $this->humidity = $subject->getHumidity();

            //也可以从推送数据中获取
            $this->pressure = $object["pressure"];
            $this->temperature = $object["temperature"];
            $this->humidity = $object["humidity"];
        }


        $this->display();
    }

    public function display()
    {
        echo "Current pressure: {$this->pressure}, Current temperature: {$this->temperature}";
    }
}

为什么要加一个 setChanged 方法

setChanged 让你在更新观察者时,有更多的弹性,能更适当的通知观察者,比方说,如果没有setCanged方法,气象站温度变化十分之一度时,都会通知所有观察者,你肯定不想让这么频繁的更新吧.我们可以控制温度变化达到一度时,调用setChanged,进行有效的更新.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/26168.html

相关文章

  • HEAD-FIRST策略模式

    摘要:这是一个设计模式系列本书所有案例均来自设计模式中文版地址欢迎大家策略模式定义了算法族,分别封装起来,让他们之间可以相互调用,此模式让算法的变化独立与调用算法的客户端。 这是一个设计模式系列,本书所有案例均来自「Head-First设计模式(中文版)」, Github地址, 欢迎大家 watch, star 策略模式 定义了算法族,分别封装起来,让他们之间可以相互调用,此模式让算法的变化...

    mikyou 评论0 收藏0
  • 学习Java必读的10本书籍

    摘要:学习编程的本最佳书籍这些书涵盖了各个领域,包括核心基础知识,集合框架,多线程和并发,内部和性能调优,设计模式等。擅长解释错误及错误的原因以及如何解决简而言之,这是学习中并发和多线程的最佳书籍之一。 showImg(https://segmentfault.com/img/remote/1460000018913016); 来源 | 愿码(ChainDesk.CN)内容编辑 愿码Slo...

    masturbator 评论0 收藏0
  • Hello CKB!

    摘要:模块链的共识配置,该配置会写入创世块。主要指责是记录和更新本地累计工作量最高的链,并维护链上数据的索引。消息使用序列化。协议是节点之间用来处理广播和转发新的交易。 by Nervos CKB Team 在 2017 年底,我们感到心里的一些想法,包括分层的网络以及一个作为共同知识库(Common Knowledge Base)的区块链,都已经成熟。因此 2018 年元旦一过我们就迫不及...

    Kerr1Gan 评论0 收藏0
  • Hello,CKB:构建加密经济网络的重要里程碑

    摘要:年,包括分层的网络以及一个作为共同知识库的区块链,都已经成熟。是一个在设计上非常不同的公有链协议,也是网络中的基础层,是整个加密经济网络的信任引擎。主要指责是记录和更新本地累计工作量最高的链,并维护链上数据的索引。 说到猿起,这些心里的想法能追溯到 2016 年,甚至更早。2017 年,包括分层的网络以及一个作为共同知识库(Common Knowledge Base)的区块链,都已经成...

    fou7 评论0 收藏0
  • JS设计模式Obeserver(察者模式、Publish/Subscribe(发布/订阅)模式

    摘要:观察者模式定义设计模式中对的定义一个对象称为维持一系列依赖于它观察者的对象,将有关状态的任何变更自动通知给它们。如图模式比较观察者模式则多了一个类似于话题调度中心的流程,发布者和订阅者解耦。 Obeserver(观察者)模式 定义 《js设计模式》中对Observer的定义:一个对象(称为subject)维持一系列依赖于它(观察者)的对象,将有关状态的任何变更自动通知给它们。 《设计模...

    荆兆峰 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<