设计模式->观察者设计模式和订阅者发布者设计模式的区别
- 一、先复习一下观察者设计模式的相关定义,优点,以及缺点
- 1.定义
- 观察者模式的三个典型例子
- 2.优点
- 3.缺点
- 4.观察者设计模式的主要角色
- 5.代码举例
- 完整代码
- 二、回答问题:观察者设计模式和订阅者发布者设计模式的区别
一、先复习一下观察者设计模式的相关定义,优点,以及缺点
1.定义
观察者模式是指多个对象存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象的状态都会得到通知并自动更新。
这种模式又叫订阅者/发布者模式(但是又与观察者略有不同,写在后面了)、模式-视图模式,他是 对象行为型模式。
观察者模式的三个典型例子
1.例子 一:
大家在学校上自习的时候,等老师走了有些人会玩手机、吃零食、交头接耳找隔壁妹妹聊天,但是被老师发现可就不好了,所以大家想了一个招:让坐在最后排的同学帮忙“放风”,老师一来就给大家一个手势通知大家,大家就继续装好好学生(放入北影教程)。
这其实就是一个典型的观察者模式:
“放风”的同学是被观察者(目标),
玩手机、吃零食的同学是观察者,大家都在观察“放风”同学(目标)的手势,一旦老师来了,被观察者(目标)就会通知大家。
2.例子 二:
csdn博客订阅就是采用观察者模式:
当博主发表新文章的时候,即博主的状态改变了变化,那些订阅的读者就会收到通知,然后进行相应的动作,比如去查看文章或者一键三连。博主与读者之间就会存在一对多的依赖关系。
3.例子 三:
事件触发模型
2.优点
1) 降低了目标与观察者之前的耦合关系。
在观察者模式中,观察者对象和主题对象之间是直接的关联关系,但是主题对象并不知道观察者对象的具体实现,而是只知道它们实现了共同的接口或抽象类。这样,主题对象和观察者对象之间的依赖关系就非常松散,它们可以独立地进行修改和扩展,而不会对彼此产生太大的影响。
2)目标与观察者之前建立了一套触发机制。
3.缺点
1)目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
解释:如果目标对象的实现发生变化,可能会影响到观察者对象的实现。这种依赖关系虽然比较松散,但并没有完全解除。
由于观察者对象需要订阅目标对象的状态变化事件,因此它们之间可能会形成循环引用。例如,当一个目标对象的状态发生变化时,它需要通知所有的观察者对象,而某些观察者对象可能需要调用目标对象的方法来获取最新的状态信息。如果这些观察者对象持有目标对象的引用,就可能出现循环引用的情况,从而导致内存泄漏等问题。
所以,为了解决以上问题,订阅者/发布者模式应运而生,订阅者/发布者模式使用了事件通道为目标和观察者搭建一个桥梁。订阅者/发布者模式(Publish-Subscribe Pattern)的一个重要特点就是使用了事件通道(Event Channel)来解除发布者(Publisher)和订阅者(Subscriber)之间的直接依赖关系,从而避免了循环引用等问题。
2)当观察者对象很多时,目标通知观察者就会花费很多时间,影响程序的效率。
4.观察者设计模式的主要角色
1)抽象主题(Subject)角色:【基类】
也叫抽象目标类,他提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
2)具体主题(Concrete Subject)角色:
3)抽象观察者(Observer)角色:【基类】
4)具体观察者(Concrete Observer)角色:
5.代码举例
// 抽象观察者(基类)
class Observer
{
public:
Observer() {}
virtual ~Observer() {}
virtual void Update() {}
};
// 抽象博客(基类)
class Blog
{
public:
Blog() {}
virtual ~Blog() {}
void Attach(Observer *observer) { m_observers.push_back(observer); } // 添加观察者
void Remove(Observer *observer) { m_observers.remove(observer); } // 移除观察者
void Notify() // 通知观察者
{
list<Observer *>::iterator iter = m_observers.begin();//遍历观察者链表
for (; iter != m_observers.end(); iter++)
(*iter)->Update();
}
virtual void SetStatus(string s) { m_status = s; } // 设置状态
virtual string GetStatus() { return m_status; } // 获得状态
private:
list<Observer *> m_observers; // 观察者链表
protected:
string m_status; // 状态
};
以上是观察者和博客(目标/被观察者/发布者),定义了通用接口。
博客类主要完成了观察者的添加、移除、通知操作。
// 具体博客类
class BlogCSDN : public Blog
{
private:
string m_name; // 博主名称
public:
BlogCSDN(string name) : m_name(name) {}
~BlogCSDN() {}
void SetStatus(string s) { m_status = "CSDN通知 : " + m_name + s; } // 具体设置状态信息
string GetStatus() { return m_status; }
};
// 具体观察者
class ObserverBlog : public Observer
{
private:
string m_name; // 观察者名称
Blog *m_blog; // 观察的博客,当然以链表形式更好,就可以观察多个博客
public:
ObserverBlog(string name, Blog *blog) : m_name(name), m_blog(blog) {}
~ObserverBlog() {}
void Update() // 获得更新状态
{
string status = m_blog->GetStatus();
cout << m_name << "-------" << status << endl;
}
};
// 测试案例
int main()
{
Blog *blog = new BlogCSDN("萧炎");
Observer *observer1 = new ObserverBlog("观察者:冰皇海波东", blog);
blog->Attach(observer1);
blog->SetStatus("发表博客<<设计模式C++实现15——观察者模式>>");
blog->Notify();
delete blog;
delete observer1;
return 0;
}
完整代码
#include <iostream>
#include <list>
using namespace std;
// 抽象观察者(基类)
class Observer
{
public:
Observer() {}
virtual ~Observer() {}
virtual void Update() {}
};
// 抽象博客(基类)
class Blog
{
public:
Blog() {}
virtual ~Blog() {}
void Attach(Observer *observer) { m_observers.push_back(observer); } // 添加观察者
void Remove(Observer *observer) { m_observers.remove(observer); } // 移除观察者
void Notify() // 通知观察者
{
list<Observer *>::iterator iter = m_observers.begin();
for (; iter != m_observers.end(); iter++)
(*iter)->Update();
}
virtual void SetStatus(string s) { m_status = s; } // 设置状态
virtual string GetStatus() { return m_status; } // 获得状态
private:
list<Observer *> m_observers; // 观察者链表
protected:
string m_status; // 状态
};
// 具体博客类
class BlogCSDN : public Blog
{
private:
string m_name; // 博主名称
public:
BlogCSDN(string name) : m_name(name) {}
~BlogCSDN() {}
void SetStatus(string s) { m_status = "CSDN通知 : " + m_name + s; } // 具体设置状态信息
string GetStatus() { return m_status; }
};
// 具体观察者
class ObserverBlog : public Observer
{
private:
string m_name; // 观察者名称
Blog *m_blog; // 观察的博客,当然以链表形式更好,就可以观察多个博客
public:
ObserverBlog(string name, Blog *blog) : m_name(name), m_blog(blog) {}
~ObserverBlog() {}
void Update() // 获得更新状态
{
string status = m_blog->GetStatus();
cout << m_name << "-------" << status << endl;
}
};
// 测试案例
int main()
{
Blog *blog = new BlogCSDN("萧炎");
Observer *observer1 = new ObserverBlog("观察者:冰皇海波东", blog);
blog->Attach(observer1);
blog->SetStatus("发表博客<<设计模式C++实现15——观察者模式>>");
blog->Notify();
delete blog;
delete observer1;
return 0;
}
//观察者:冰皇海波东-------CSDN通知 : 萧炎发表博客<<设计模式C++实现15——观察者模式>>
二、回答问题:观察者设计模式和订阅者发布者设计模式的区别
这两种模式之间的主要区别可以参考下图:
可以这么回答:
-
在23种基本的设计模式并没有订阅-发布模式,如果非要说有什么区别的话,我认为,订阅者发布者设计模式是观察者模式的变体,它们大部分是一致的,有很多相似之处,但并不完全相同,具体的区别有如下几点:
-
观察者模式里只有两个角色:观察者和被观察者。
而,对于订阅发布者模式而言,它则有三种角色:发布者、订阅者、调度器(第三者->事件通道(Event Channel))。 -
在观察者(Observer)模式中,观察者(Observers)角色知道抽象主题(Subject)角色,同时 , 抽象主题(Subject)角色 还保留了 观察者(Observers)角色 的记录。
然而,在发布者/订阅者中,发布者和订阅者不需要彼此了解。他们只是在消息队列或代理的帮助下进行通信。
在 订阅者/发布者(Subscriber / Publisher)模式中,组件是松散耦合(即彼此之间的依赖关系比较少,它们可以独立地进行修改和扩展,而不会对彼此产生太大的影响。)的;相比Observer模式,订阅者发布者模式的耦合更低。 -
观察者模式主要以同步方式实现,即当某些事件发生时,Subject调用其所有观察者的适当方法。发布者/订阅者在大多情况下是异步方式(使用消息队列)。
-
观察者模式需要在单个应用程序地址空间中实现。而订阅者/发布者模式更像是跨应用程序模式。