• 设计模式学习总结:(4)观察者模式


    现实生活中,这样的例子太多了,一个对象的状态受另外一个对象的影响。比如,进度条根据上传的百分比而变化,红灯停绿灯行。。。。。这样的业务数不胜数。甚至我们有时候心情也是随着很多经历而变化。在开发过程中,这样的业务当然也是很多的,但是,稍有不慎,我们可能会实现出比较麻烦的代码。而设计模式中有一种模式对于解决这样的业务具有很好作为,那就是观察者模式。简而言之就是:我们有一个目标,还有很多观察者,当目标变化,观察者们将作出相关的变化。

    观察者模式:

    看看书上的定义,也是行为模式

    意图:

    定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变,所有依赖于它的对象都得到通知并将自动更新。


    对于这样一种业务,我们很自然的需要两种角色:目标观察者

     确实,为了遵循"开闭"原则,我们两种对象创建两种对象的抽象基类,这样便于扩展和维护。于是就有四个基本的结构。

    抽象目标抽象观察者具体目标具体观察者

    书上给出的结构图:

    对这个结构图,需要注意到是目标的  Notify()和 观察者的 update()两个方法。

    抽象代码可以实现为:

    抽象目标:

    class AbtSubject
    {
    private:
        vector<AbtObserver *>  _obArr;   //抽象目标有观察者
    public: virtual void addObserver(AbtObserver *observer); //自己实现 virtual void rmObserver(AbtObserver *observer); //要实现 virtual void notify(); } void AbtSubject::notify() { for(auto obs:_obArr) { obs.update(); } }

    注:虚析构和构造函数未给出。

    抽象观察者:

    class AbtObserver
    {
    protect:
        AbtObserver();
        virtual ~AbtObserver();
    public:
        virtual void update()=0;
    }

    具体目标,假如是红绿灯:

    enum RGcolor
    { red, green, yellow }
    class RGLight:public AbtSubject { private: RGcolor _color; public: RGcolor getColor() { return _color; } RGcolor setColor(RGcolor color) { _color = color; }
    void colorChange()
    {
    notify();
    }
    }

    简单的实现:

    具体观察者:

    #include<iostream>
    //如果有必要,引入subject头文件
    
    class WalkingPerson()
    {
    private:
        AbSubject * _subject;
    public:
        void update();
        void walk();
        void stop();
    }
    void WalkingPerson::update()
    {
        switch(_subject->getColor())
        {
            case red:
                 this->stop();
                 break;
            case green:
                 this->walk();
                 break;
             }
    }

    这样具体思路也就是这样,当然还有很多细节,比如构造函数的实现,这可能还得通过具体业务额定。至少到这里我们已经知道这样四个角色的含义了。

    抽象目标:

    自然的包含了一系列的观察者,定义了一些抽象接口,比如通知函数notify,用来通知自己拥有的那一系列观察者。和一些后代接口,比如添加删除观察者。

    抽象观察者:

    定义如何反应目标的通知的抽象方法update()

    具体的目标:

    具体实现。

    具体观察者:

    通过包含自己观察者来保存与目标的状态。

    这样的一个结构很明显的好处就是:

    1.目标和观察者耦合度最小:因为都是建立在两个抽象类之上,通过多态特性,可以把两种不同类之间的耦合度达到最小,可以动态 添加具体观察者而不影响其他观察者。

    2.广播优势:也就是说发出信号而无需知道观察者是谁,一视同仁。

    缺陷也很明显:

    确实存在一种依赖关系,这种依赖关系可能导致未知影响,甚至是对于观察者的依赖对象的变化。还有目前的目标假设在一个,有时候存在多种目标,而需要观察者知道是哪个目标。

    好处可以通过举返利,比如将person类耦合到目标类中,但是这样的违背了面向对象的原则,这里就不举例了。

    设计模式:可复用面向对象软件的基础 这本书上还给了一些在实际开发过程中额外的复杂的依赖关系。

    1."创建目标到其观察者之间的映射":这是作者假设如果目标过多而观察者比较少,额外的空间开销显得没有必要,所以通过映射(hash)方式来处理会更好,但是也可能造成观察者的开销。

    2."观察多个观察者",我们上面的例子是只有一个subject,如果有多个,我们可能还需要将具体suject作为update的参数,这样观察者可以以此判断是什么目标发生变化。

    3."判断是谁的更新",这个嘛,看了很久,上面例子中,我是在红绿灯颜色发生变化的时候调用的notify方法,也就是书上的第一个方案,有具体subject自己决定什么时候调用notify通知观察者,这种的坏处是可能存在效率问题。第二种,是有客户(使用者)自己去调用,这种方式坏处是增加客户责任,同时使用者的对要求也比较高。

    4."对已经删除目标悬挂引用",就是说如果有个目标对象被删除,如何处理其相关观察者引用这个被删除对象的问题。

    5."发出通知前确保目标状态的一致性",在发送通知的时候很可能目标和观察者直接的状态并不统一。

    6."避免特定于观察者的更新协议——推/拉模型",推模型:广播由目标向观察者发送改变的信息。拉模型:有观察者自己询问改变信息。

    7."显示指定感兴趣的信息",可以在添加的时候讲观察者感兴趣的信息告诉目标,更新的时候就传给目标。

    8."封装复杂的更新",当目标和观察者之间的依赖关系过于复杂,应该建立一个中间对象来处理这种复杂的关系,书上给出了结构图。

    可以看到,在目标和观察之间建立了一个manage对象。在理解过程中对一些业务其实还处在朦胧阶段,我想也许在将来的工作中,可以加深对该模式的理解,共勉。

  • 相关阅读:
    C语言中 单引号与双引号的区别
    Linux主分区,扩展分区,逻辑分区的联系和区别
    fdisk
    df du 的区别
    filesystem
    git clone
    curl
    HDR 高动态范围图像
    source ~/.bashrc 什么意思
    linux 挂载
  • 原文地址:https://www.cnblogs.com/wuweixin/p/5425099.html
Copyright © 2020-2023  润新知