设计模式 行为型模式 观察者模式
观察者模式
直观的理解,就是发布-订阅
模式.
Subject 对应 Publisher, Observer 对应 Subscriber.
但是其实发布-订阅
只是观察者模式的一种特殊情况,它更强调的是:
一个对象或行为的变化导致其他对象状态的改变,或者通知其他对象的改变状态.他们之间将产生联动,一个对象的改动将影响其他对象.
书中用了某成员被攻击, 同时通知其他所有成员的例子.这就是典型的对象联动
.
//典型的订阅行为,有改变可以通过notifyAll通知所有订阅者
Subject::notifyAll() {
for(auto i : this->members) {
i->notify(this->status);
}
}
//核心就是参数传进来的sub,实现对象联动
Observer::beAttacked(Subject * sub) {
sub->notifyAll();
}
subject 可以添加 observer,反过来,用一个 observer 也应该可以添加不同的 subject.
MVC
也是典型的观测者模式设计.
用一个典型的文章订阅作为例子:
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <memory>
#include <set>
#include <list>
/**
*
*
* 博客分不同主题,
* 不同主题下有不同的订阅者.实现以下几个目标
*
* 1. 订阅者本身也是文章的发布者,可以发布一篇多个主题的文章;
* 2. 当某个主题的文章更新时,通知所有相关订阅者有新的文章;
* 3. 订阅者 也可以 订阅多个主题的文章.
* 4. 订阅者可以关注其他订阅者, 当其他人发表新文章时,通知自己.
*
*
* */
class Observer;
class Subject;
class Article {
public:
void setId(int id) {article_id = id;}
int getId() {return article_id;}
void setTitle(std::string t) {title = t;}
const std::string & getTitle() {return title;}
void setAuthor(std::shared_ptr<Observer> ob) {author = ob;}
std::shared_ptr<Observer> getAuthor() {return author;}
void addSubjects(std::shared_ptr<Subject> subject) {
subjects.insert(subject);
}
std::set<std::shared_ptr<Subject>> getSubjects() {
return subjects;
}
private:
//id
int article_id;
//标题
std::string title;
// 作者
std::shared_ptr<Observer> author;
//文章可以属于多个不同主题
std::set<std::shared_ptr<Subject>> subjects;
};
class Observer;
class Subject {
public :
Subject(const std::string & topic): m_topic(topic){}
virtual ~Subject(){}
void setTopic(const std::string & topic){
m_topic = topic;
}
//通知订阅者消息
virtual void notifyAll() {}
//在主题下添加了新文章
virtual void addArticle(std::shared_ptr<Article> art) {
m_articles.insert(
std::make_pair(art->getId(),art)
);
}
//在主题下去掉文章
virtual void removeArticle(std::shared_ptr<Article> art) {
auto found = m_articles.find(art->getId());
if (found != m_articles.end()) {
m_articles.erase(found);
}
}
void addSubscriber(const Observer & ob){
m_obs.push_back(ob);
}
//void removeSubscriber(const Observer & ob){
//m_obs.remove(ob);
//}
const std::string & getTopic() {return m_topic;}
std::list<Observer> getObservers() {
return m_obs;
}
private:
//谁订阅了该主题
std::list<Observer> m_obs;
//什么主题
std::string m_topic;
//发表在该主题下的文章
std::map<int, std::shared_ptr<Article>> m_articles;
};
class Observer {
public:
Observer(std::string name):m_name(name) {}
virtual ~Observer(){}
//收到通知
virtual void notified(std::shared_ptr<Article> art) {
std::cout << m_name << " got new articles:(" <<
art->getTitle() << ")" << std::endl;
}
//个人订阅什么主题
virtual void subsTopic(std::shared_ptr<Subject> sub) {
sub->addSubscriber(*this);
}
//个人关注的其他人
virtual void watchSomebody(const std::shared_ptr<Observer> & ob) {
//把关注对象保存
watch_objs.push_back(*ob);
//还要把自己添加到别人的bewatched list里
//auto & f = ob->getFollowLists();
//这两个就是不同的东西, 所以并没有改变ob的followers
auto & f = ob->followers;
//std::cout << & ob->followers << std::endl;
//std::cout << &f << std::endl;
f.push_back(
//std::shared_ptr<Observer>(this)
std::make_shared<Observer>(*this)
//*this
);
std::cout << ob->getName() << " add followers:" <<
this->getName() << std::endl;
std::cout << f.size() << std::endl;
}
void setName(const std::string & name) {m_name = name;}
const std::string & getName() {return m_name;}
//个人被哪些人关注.
std::list<std::shared_ptr<Observer>> followers;
//个人关注的其他作者
std::list<Observer> watch_objs;
private:
std::string m_name;
};
//一个write, 可以发布文章到主题上
class Writer : public Observer {
public: Writer(std::string name) : Observer(name) {}
void pubNewArticle(std::shared_ptr<Article> article) {
std::cout << getName() << " published a new article." << std::endl;
std::cout << "title:
std::cout << "===============" <<std::endl;
auto subjects = article->getSubjects();
//遍历文章主题
for (auto s : subjects) {
auto obs = s->getObservers();
//遍历主题下对应的订阅者
for (auto o : obs) {
//逐个通知
o.notified(article);
}
}
//遍历我的粉丝
std::cout << "followers size:" << followers.size() << std::endl;
if (followers.size()) {
std::cout << getName() << " has followers.." << std::endl;
}
for (auto f: followers) {
f->notified(article);
}
}
};
//一个reader,只订阅主题,
class Reader : public Observer {
public:
Reader(std::string name) : Observer(name) {}
};
int main(int argc , char* argv[])
{
auto topic_adventure = std::make_shared<Subject>("Adventure");
auto topic_love = std::make_shared<Subject>("Love story");
auto topic_travel = std::make_shared<Subject>("Travel");
auto hans = std::make_shared<Writer>("Mr Hans");
auto tommy = std::make_shared<Reader>("Mr Tommy");
tommy->subsTopic(topic_adventure);
auto derk = std::make_shared<Reader>("Mr Derk");
derk->subsTopic(topic_adventure);
auto handsome = std::make_shared<Writer>("Mr Handsome");
handsome->subsTopic(topic_adventure);
handsome->subsTopic(topic_love);
auto zombie = std::make_shared<Reader>("Miss Zombie");
zombie->subsTopic(topic_adventure);
zombie->subsTopic(topic_love);
//僵尸小姐关注了handsome先生, 无论handsome的什么主题的文章都感兴趣..
zombie->watchSomebody(handsome);
auto a = std::make_shared<Article>();
a->setId(0);
a->setTitle("Time travel to Mars");
a->setAuthor(hans);
a->addSubjects(topic_adventure);
auto b = std::make_shared<Article>();
b->setId(1);
b->setTitle("A black hole in sky");
b->setAuthor(hans);
b->addSubjects(topic_adventure);
hans->pubNewArticle(a);
std::cout << std::endl;
hans->pubNewArticle(b);
auto c = std::make_shared<Article>();
c->setId(1);
c->setTitle("Senven days in Chengdu");
c->setAuthor(handsome);
c->addSubjects(topic_travel);
std::cout << std::endl;
handsome->pubNewArticle(c);
return 0;
}
结果
Mr Handsome add followers:Miss Zombie
1
Mr Hans published a new article.
Title: Time travel to Mars
===============
Mr Tommy got new articles:(Time travel to Mars)
Mr Derk got new articles:(Time travel to Mars)
Mr Handsome got new articles:(Time travel to Mars)
Miss Zombie got new articles:(Time travel to Mars)
followers size:0
Mr Hans published a new article.
Title: A black hole in sky
===============
Mr Tommy got new articles:(A black hole in sky)
Mr Derk got new articles:(A black hole in sky)
Mr Handsome got new articles:(A black hole in sky)
Miss Zombie got new articles:(A black hole in sky)
followers size:0
Mr Handsome published a new article.
Title: Senven days in Chengdu
===============
followers size:1
Mr Handsome has followers..
Miss Zombie got new articles:(Senven days in Chengdu)