zoukankan      html  css  js  c++  java
  • 【设计模式系列】行为型模式之Observer模式

    概要
    Observer模式算是一种大名鼎鼎的设计模式了,如果你还没听说过Observer模式,那你总多少听说过MVC模式吧?其实MVC就是基于Observer模式的细化和扩展。所以如果要理解MVC,就应该首先掌握Observer模式。Observer模式反映的是一种面向对象的一对多的事件触发关系,当某个对象希望在发生某种变化时能通知其他多个对象,而这个对象又不希望跟他希望通知的其他对象产生耦合时,Observer模式会是一种很好的解决方法。

    目的
    在对象间建立一对多的对应关系,当发生某种变化时可以通知已建立关系的多个对象。而对象间本身不产生任何耦合。

    实例
    Observer模式的例子其实有很多,所有涉及事件通知的机制几乎都可以使用Observer模式来实现。在这里就看这样一个例子吧。
    假设我们有一个系统,当有任何用户登录成功时,都需要触发如下动作:
    1. 显示该用户信息到页面UI中
    2. 在Database中为该用户创建用户空间
    3. 广播给其他已登录用户该用户登录了
    4. 其他

    千万不要告诉我,你会这样设计:直接从Login模块去强关联其他需要动作的模块。
    这样的设计把所有模块搞的一团浆糊,会让以后的任何扩展,变更都举步维艰。看看Observer模式会怎么做吧。

    class Observer {
    public:
         virtual void Update(int event) = 0;
    };

    所有需要被触发的对象都从Observer类继承,并重写Update方法。

    const int EVENT_LOGIN = 1;
    class UIMng : public Observer {
    public:
         virtual void Update(int event) {
              if (EVENT_LOGIN  == event) {
                   ......          
              }
         }
    };
    class DatabaseMng: public Observer {
    public:
         virtual void Update(int event);
    };
    class BroadcastMng: public Observer {
    public:
         virtual void Update(int event);
    };
    DatabaseMng, BroadcastMng等都和UIMng类类似。
    
    class Subject {
    public:
         void attach(Observer* o) {
              if (o != NULL) {
                  mObservers.push_back(o);
              }
         }
         void detach(Observer* o) {
              list<Observer*>::iterator it;
              if (o != NULL) {
                   for (it = mObservers.begin(); it !=mObservers.end(); it++) {
                        if (*it == o) {
                            mObservers.erase(it);
                        }
                   }
              }
         }
         void Notify(int event) {
              list<Observer*>::iterator it;
              for (it =mObservers.begin(); it !=.end(); it++) {
                   if (*it != NULL) {
                        (*it)->Update(event);
                   }
              }
         }
    private:
         list<Observer*> mObservers;
    };

    Subject类提供了attach和detach方法来增加或删除需要绑定的观察者对象,而Notify方法则会触发执行所有已绑定对象的Update方法。需要Observer模式支持的可以从Subject类继承。

    class LoginSubject : public Subject {
    public:
         void OnLogin() {
              Notify(EVENT_LOGIN);
         }
    };

    login成功调用OnLogin方法时会触发所有被绑定的对象。

    client调用方代码如下:
    Observer* uiMng = new UIMng();
    Observer* dbMng = new DatabaseMng();
    Observer* bcMng = new BroadcastMng();
    LoginSubject login;
    login.attach(uiMng);
    login.attach(dbMng );
    login.attach(bcMng );

    当login对象的OnLogin被调用时,所有绑定的Observer对象都会被触发并调用其Update方法来进行响应。

    上面的实例中从Subject把事件类型传递给了Observer,当Observer被绑定于多种事件时,可以通过传递事件类型类进行区别处理。其实很多时候索性定义多种Observer对象会让逻辑更清晰,比如这里是login事件的话就把这种Observer定义为LoginObserver,当还需要其他事件观察者时,再定义其他观察者。
    class LoginObserver{
    public:
         virtual void Update() = 0;
    };

    而在实际应用中,从Subject到Observer数据的流动通常有两种方式,
    一种是Push的方式,Push方式是指数据被Subject主动的传递给了Observer,比如上面实例中Subject
    就通过参数直接把事件类型传递给了Observer。

    另一种是Pull的方式,Pull方式相反,它是由Observer主动从Subject获取相关数据。代码会有如下变化:
    class Subject {
    public:
         void attach(Observer* o) {
              if (o != NULL) {
                  mObservers.push_back(o);
              }
         }
         void detach(Observer* o) {
              list<Observer*>::iterator it;
              if (o != NULL) {
                   for (it = mObservers.begin(); it !=mObservers.end(); it++) {
                        if (*it == o) {
                            mObservers.erase(it);
                        }
                   }
              }
         }
         void Notify() {
              list<Observer*>::iterator it;
              for (it =mObservers.begin(); it !=.end(); it++) {
                   if (*it != NULL) {
                        (*it)->Update(this);
                   }
              }
         }
         int GetStatus() {
              ......
         }
    private:
         list<Observer*> mObservers;
    };
    class UIMng : public Observer {
    public:
         virtual void Update(Subject* sub) {
              ......
              int status = sub->GetStatus();
              ......
         }
    };

    在Notify中,每次调用Update都把Subject对象本身传递过去,而在Update方法中会有Observer主动去Pull相关数据。上面代码中UIMng取得了Subject中的Status信息。

    Push方式的特点是,不管Observer是否需要都会把数据Push给Observer,处理逻辑简单,但当数据量大时多少会影响性能。
    Pull方式的特点是,由Observer根据自己的需求自己去从Subject取数据,可以忽略不需要的数据,避免冗余,但处理逻辑较复杂,比较容易出错。

    应用
    前面也已经提到过,Observer模式的应用极广,很多一对多的响应逻辑几乎都可以用Observer模式来解决。





  • 相关阅读:
    内置函数
    打印进度条
    生成器表达式
    Linux(CentOS7)安装Tomcat (Tomcat+JDK)
    IDEA左侧文件目录不见了,帮你找回来!
    前端插入date类型的数据到数据库
    Java Web制作登录 验证码
    Java使用数据库连接池连接Oracle数据库
    Java Web项目实现写日志功能
    IDEA编写JavaWeb出现乱码,成功解决!
  • 原文地址:https://www.cnblogs.com/secbook/p/2655106.html
Copyright © 2011-2022 走看看