观察者模式

68次阅读

共计 2125 个字符,预计需要花费 6 分钟才能阅读完成。

简介

观察者模式,定义了对象间一对多的关系,当被观察者的状态改变时,它所依赖的所有对象都会被更新和通知。观察者模式在实际应用中较多,例如各种订阅的实现都有观察者的应用,mvc 复合模式也使用了观察者。

UML 类图

示例

有球迷关注体育新闻,球迷可以作为观察者,订阅服务可以看作 Subject,以此为例,实现观察者服务。
观察者相关,observer.h

#ifndef OBSERVER_H
#define OBSERVER_H
#include <set>
#include <string>
#include <iostream>

using namespace std;
class CObserver;
class CSubject
{
public:
    virtual void Attach(CObserver* pObserver);
    virtual void Detach(CObserver* pObserver);
    virtual void SetNews(const string& strContent) = 0;
    
protected:
    set<CObserver*> m_observerContainer;
};

class CSportSubject:public CSubject
{
public:
    virtual void Notify();
    void SetNews(const string& strContent);
    string& GetNews();
private:
    string m_strNews;
};

class CObserver
{
public:
    virtual void update(string& strContent) = 0;
    virtual void Register(CSubject* pSubject);
    virtual void UnRegister(CSubject* pSubject);
protected:
    string m_strContent;
};

class CSportObserver:public CObserver
{
public:
    CSportObserver(const string& strName):m_strName(strName){}
    void update(string& strContent);
private:
    string m_strName;
};

#endif

观察者具体实现,observer.cpp

#include "observer.h"

void CSubject::Attach(CObserver* pObserver)
{m_observerContainer.insert(pObserver);
}

void CSubject::Detach(CObserver* pObserver)
{m_observerContainer.erase(pObserver);
}

void CSportSubject::Notify()
{for(set<CObserver*>::iterator iter = m_observerContainer.begin(); iter != m_observerContainer.end(); ++iter)
    {(*iter)->update(m_strNews);
    }
}

void CSportSubject::SetNews(const string& strContent)
{
    m_strNews = strContent;
    Notify();}

string& CSportSubject::GetNews()
{return m_strNews;}
void CObserver::Register(CSubject* pSubject)
{pSubject->Attach(this);
}

void CObserver::UnRegister(CSubject* pSubject)
{pSubject->Detach(this);
}

void CSportObserver::update(string& strContent)
{
    m_strContent = strContent;
    cout<<m_strName<<":"<<strContent<<endl;
}

客户端调用,main.cpp

#include "observer.h"

#define SAFE_DELETE(p) if(p){delete (p); (p) = NULL;}
int main(int argc, char* argv[])
{
    CSubject* pSportSubject = new CSportSubject;
    CObserver* pObserverA = new CSportObserver("FansA");
    CObserver* pObserverB = new CSportObserver("FansB");
    pObserverA->Register(pSportSubject);
    pObserverB->Register(pSportSubject);

    pSportSubject->SetNews("Messi goal 3.");
    SAFE_DELETE(pObserverA);
    SAFE_DELETE(pObserverB);
    SAFE_DELETE(pSportSubject);
    return 0;
}

正文完
 0

观察者模式

68次阅读

共计 159 个字符,预计需要花费 1 分钟才能阅读完成。

观察者模式又称订阅 - 发布模式,是一种一对多的依赖关系,多个观察者对象可同时监听某一主题对象,当该主题对象状态发生变化时,相应的所有观察者对象都可收到通知。

在 getHumidity 中会调用 notifyObservers 来通知所有 observers,这里遵循了依赖倒置原则,被观察类(主题)依赖于抽象观察者而非具体观察者。

正文完
 0

观察者模式

68次阅读

共计 4731 个字符,预计需要花费 12 分钟才能阅读完成。

观察者模式 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

这就好比一个办在线教育的老师 (OnlineTeacher) 和线下的若干学生 (localStudent1, localStudent2, localStudent3 …) 之间的关系。

当这个老师在线上发布了一条消息之后,他所有的学生都会收到通知,并可以根据这条消息的内容来实现对应的更新。

观察者模式类图

为了实现 松耦合 设计,OnlineTeacher 对象只需要实现 Subject 接口,并实现其中规定的三个方法即可:

  1. registerObserver(); // 注册观察者
  2. removeObserver(); // 取消注册观察者
  3. notifyObserver(); // 通知观察者

Subject 接口:

public interface Subject {
    //Subject 接口中的三个方法, 用于注册、移除和通知 observers
    void registerObserver(Observer observer);

    void removeObserver(Observer observer);
    // 以上两个方法都需要一个 observer 对象作为变量,以实现注册或被删除

    void notifyObservers();}

同时,OnlineTeacher 对象还需要一个数据结构,来存储已经注册的学生对象。

OnlineTeacher 类

public class OnlineTeacher implements Subject {
    // 建立一个数据结构来存储注册过的 observer 对象
    private ArrayList<Observer> observerArrayList;
    // 用 message 来模拟老师讲课
    private String message;

    //OnlineTeacher 类的构造器,生成该对象时,会初始化一个 observerArrayList
    public OnlineTeacher() {observerArrayList = new ArrayList<>();
    }

    @Override
    public void registerObserver(Observer observer) {observerArrayList.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {int i = observerArrayList.indexOf(observer);
        if (i > 0) {observerArrayList.remove(i);
        }
    }

    // 实现 notifyObservers(),实质上就是遍历 observerArrayList,让其中的每一个 observer 对象调用他们的 update()方法
    @Override
    public void notifyObservers() {for (int i = 0; i < observerArrayList.size(); i++) {observerArrayList.get(i).updates(message);
        }
    }

    //OnlineTeacher 有一个方法,setMessage()
    public void setMessage(String newMessage) {
        this.message = newMessage;
        messageChanged(); //message 赋值之后,调用 messageChanged()方法
    }

    // 还有一个方法,messageChanged, 在此方法中调用 notifyObservers()
    public void messageChanged() {notifyObservers();
    }
}

而所有的学生对象只需要实现 Observer 接口即可成为“观察者”。
所有的观察者只需要保有一个 OnlineTeacher 对象的引用,便可以在各自的构造器中实现对自身的注册。

Observer 接口

public interface Observer {void updates(String message);
}

用来模拟的 localStudent 对象们

localStudent1 类:

public class LocalStudent1 implements Observer {

    private Subject onlineTeacher;

    // 构造器需要 OnlineTeacher 对象(也就是 subject),用来注册自己
    public LocalStudent1(Subject onlineTeacher) {
        this.onlineTeacher = onlineTeacher;
        onlineTeacher.registerObserver(this);

    }


    @Override
    public void updates(String message) {
        System.out.println("localStudent1 从 onlineTeacher 那儿得到的 message 是:" +
                message +','+
                "我领悟到的是: 女子和小人一样");
    }
}

localStudent2 类:

public class LocalStudent2 implements Observer {
    private Subject onlineTeacher;

    public LocalStudent2(Subject onlineTeacher) {
        this.onlineTeacher = onlineTeacher;
        onlineTeacher.registerObserver(this);
    }

    @Override
    public void updates(String message) {
        System.out.println("localStudent2 从 onlineTeacher 那儿得到的 message 是:" +
                message +','+
                "我领悟到的是: 这话还有后半句——近之则不逊,远之则怨");
    }
}

localStudent3 类:

public class LocalStudent3 implements Observer{

    private Subject onlineTeacher;

    public LocalStudent3(Subject onlineTeacher) {
        this.onlineTeacher = onlineTeacher;
        onlineTeacher.registerObserver(this);
    }

    @Override
    public void updates(String message) {
        System.out.println("localStudent3 从 onlineTeacher 那儿得到的 message 是:" +
                message +','+
                "我领悟到的是: 一个人对他亲近了,他对你不尊重,疏远了却又有怨言!");

    }
}

运行ObserverPatternRunDemo.java

public class ObserverPatternRunDemo {public static void main (String [] args){OnlineTeacher onlineTeacher = new OnlineTeacher();

        LocalStudent1 localStudent1 = new LocalStudent1(onlineTeacher);
        LocalStudent2 localStudent2 = new LocalStudent2(onlineTeacher);
        LocalStudent3 localStudent3 = new LocalStudent3(onlineTeacher);

        onlineTeacher.setMessage("子曰:唯女子与小人难养也");
    }


}

运行时,以 localStudent1 对象为例。生成该对象时,会调用 onlineTeacher 对象的 registerObserver() 方法 ,将其自身加入到 onlineTeacher 对象的observerArrayList 当中。
当 onlineTeacher 对象调用 setMessage("子曰:唯女子与小人难养也"); 时,相当于 message 的值为 子曰:唯女子与小人难养也

//OnlineTeacher.java 中定义的 setMessage()和 messageChanged()方法
...
  //OnlineTeacher 有一个方法,setMessage()
    public void setMessage(String newMessage) {
        this.message = newMessage;
        messageChanged(); //message 赋值之后,调用 messageChanged()方法
    }

    // 还有一个方法,messageChanged, 在此方法中调用 notifyObservers()
    public void messageChanged() {notifyObservers();
    }
}
...

根据 onlineTeacher 类中定义的方法,应有:
先调用 messageChanged() 方法 ,然后会调用notifyObservers() 方法 , 而notifyObservers() 方法会遍历所有的 Observer 对象,并调用他们的 update() 方法:

  // 实现 notifyObservers(),实质上就是遍历 observerArrayList,让其中的每一个 observer 对象调用他们的 update()方法
    @Override
    public void notifyObservers() {for (int i = 0; i < observerArrayList.size(); i++) {observerArrayList.get(i).updates(message);
        }
    }

3 个 localStudent 对象的 update() 方法
localStudent1.update();

   @Override
    public void updates(String message) {
        System.out.println("localStudent1 从 onlineTeacher 那儿得到的 message 是:" +
                message +','+
                "我领悟到的是: 女子和小人一样");
    }
}

localStudent2.update();

  @Override
    public void updates(String message) {
        System.out.println("localStudent2 从 onlineTeacher 那儿得到的 message 是:" +
                message +','+
                "我领悟到的是: 这话还有后半句——近之则不逊,远之则怨");
    }
**localStudent3.update();**
   @Override
    public void updates(String message) {
        System.out.println("localStudent3 从 onlineTeacher 那儿得到的 message 是:" +
                message +','+
                "我领悟到的是: 一个人对他亲近了,他对你不尊重,疏远了却又有怨言!");

    }

Demo 运行的结果

localStudent1 从 onlineTeacher 那儿得到的 message 是:子曰:唯女子与小人难养也, 我领悟到的是: 女子和小人一样
localStudent2 从 onlineTeacher 那儿得到的 message 是:子曰:唯女子与小人难养也, 我领悟到的是: 这话还有后半句——近之则不逊,远之则怨
localStudent3 从 onlineTeacher 那儿得到的 message 是:子曰:唯女子与小人难养也, 我领悟到的是: 一个人对他亲近了,他对你不尊重,疏远了却又有怨言!

正文完
 0

观察者模式

68次阅读

共计 2654 个字符,预计需要花费 7 分钟才能阅读完成。

概述
组成
​ 主题 + 订阅者(观察者)= 观察者模式​ 主题“接口”(自定义或者 Observable 类):remove(Object o);add(Object o);notify();​ 订阅者”接口“(自定义或者 Observer 接口):update();
定义
​ 观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它所有的依赖者都会收到通知并自动更新。但注意的是,这种一对多的关系并不是绝对的,一个观察者同样可以观察多个主题。
设计原则
​ 为了交互对象之间的松耦合而努力。松耦合的设计之所以能让我们建立有弹性的 OO 系统,能够够应对变化,是因为对象之间的互相依赖降到了最低。
​ 松耦合: 两个对象松耦合,它们依然可以交互,但是不清楚彼此的细节。
​ 观察者模式提供了一种对象设计,让主题和观察者之间松耦合:关于观察者的一切,主题只知道观察者实现了某个接口(也就是 Observer 接口)。主题不需要知道观察者的具体类型是哪种、做了些什么或其他任何细节。
实现
​ 实现观察者模式的方式不止一种,但其中以包含 Subject 和 Observer 接口的类设计最为常见。
​ 注意:在例子中还提供了主题是基类观察者是接口的实例,不推荐的主要原因是继承类只能继承一个这里继承了就无法继承其他的。
​ 观察者模式有主题推送和观察者获取两种类型:
订阅 / 取消功能
​ 无论是对于主题推送类型还是观察者获取类型,都有一个这样的功能的实现:订阅者进行订阅和取消订阅。为什么都是要由订阅者来做,这就是上面的松耦合的设计原则,主题不需要知道订阅者的状态,只需要知道它实现了订阅者接口。为了实现这个功能,首先需要在主题的接口或者基类中中定义相应的 public 的订阅和取消订阅的方法,比如 api 中 Observable 类所实现的:
` public synchronized void addObserver(Observer o) {​ if (o == null)​ throw new NullPointerException();​ if (!obs.contains(o)) {​ obs.addElement(o);​ }​ } `
`public synchronized void deleteObserver(Observer o) {​ obs.removeElement(o);​ } `
`public synchronized void deleteObservers() {​ obs.removeAllElements();​ }`
​ 上面的这些方法已经能够让订阅者实现自行订阅的不同主题功能 (当订阅者只订阅一个主题时,订阅可以放在构造函数中来实现),除此之外,如果想要订阅者能够取消订阅,还需要在订阅者对象中保留相应主题对象的引用(在 api 中 Observer 是一个接口),当要取消订阅的时候直接通过主题引用调用 deleteObserver(this) 方法即可。这样就能够实现订阅者自己来进行订阅主题和取消订阅主题。当一个订阅者订阅多个具体主题实现对象的时候,可以考虑使用容器来进行管理,在取消订阅的时候也可可以遍历容器找到相应的 Class 类型的主题对象或者找到相同的的主题对象,然后再地调用 deleteObserver(this)即可。
​ api 中订阅者和主题两者接口 / 基类中的成员,如下图所示,在主题基类中可以看到除了我们上面说的:通知、添加订阅者、删除订阅者这些方法之外,还有一系列关于 changed 成员变量的方法,为什么要引入这个布尔变量呢?原因参考 Why do I need to call setChanged before I notify the observers?

主题推送类型
​ 主题推送在自定义的的时候就是通过 notify()在内部遍历订阅者数组将全部参数统统发给所有订阅者,在这里因为不同订阅者所需的参数不同,所以必须将所有参数都传递过去。订阅者中设置了与要接受的某些参数对应类型的成员变量。(实例可参考)​ 主题推送在基于 api 实现的时候就是当参数改变时通过调用 notifyObservers(Object arg)将参数放在 arg 里。在这里因为不同订阅者所需的参数不同,所以必须将所有参数都传递过去。然后订阅者在重写的 update(Observable o, Object arg)里去取就行,其他的和自定义基本相同。其中第一个 Object o 是将主题的引用传递过去了,这是为了让观察者能够知道是哪个主题的通知它的。因为一个观察者可以观察多个主题。
观察者获取类型
​ 观察者获取在自定义的时候就是在主题的具体实现里添加上所有数据的 get 方法(因为数据都是私有的),然后在订阅者在重写的当中 update(Object o); 其中 o 是将主题的引用传递过去,通过主题引用来调用 get 方法获取所想要的参数(不一定是全部了)。当然也可以不这样实现 update,直接只是 update(),但是需要在订阅者对象中设置有主题对象的引用,同样是根据引用调用 get 方法获取参数,但这样的做法缺点在于不利于应对一个订阅者订阅多个主题对象的情况。
​ 观察者获取在基于 api 实现的时候就是当参数改变时通过调用 notifyObservers()从而会调用 notifyObservers(null),然后调用订阅者重写的 update(Observable o, null)方法里来进一步通过主题的引用来调用 get 方法获取所想要的参数(不一定是全部了)(实例可参考)。其中第一个 Object o 是将主题的引用传递过去了,这是为了让观察者能够知道是哪个主题的通知它的。因为一个观察者可以观察多个主题。
基于 API 实现的缺点
​ 基于 api 实现主题有个显著的缺点,因为 Observable 是一个类,而不是接口,如果继承了 Observable 类,那就无法继承其他类了。如果必须要继承其他类,那还是需要自行来实现主题接口和主题的具体实现类
优点
​ 任何时候我们都可以随时增加观察者,因为主题唯一依赖的东西是一个实现 Observer 接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用新的观察者取代旧的观察者,主题不会受到影响。同样,也可以在任何时候删除观察者。
​ 有新类型的观察者出现时,主题的代码不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所要做的是在新的类里实现此观察者接口,然后注册为观察者即可。
​ 改变主题或观察者其中一方,并不会影响另一方。因为两者是松耦合的,所以只要他们之间的接口仍被遵守,我们就可以自由地改变他们。

正文完
 0