观察者模式

直观的理解,就是发布-订阅模式.

Subject 对应 Publisher, Observer 对应 Subscriber.

但是其实发布-订阅只是观察者模式的一种特殊情况,它更强调的是:

一个对象或行为的变化导致其他对象状态的改变,或者通知其他对象的改变状态.他们之间将产生联动,一个对象的改动将影响其他对象.

2018-08-13-10-57-35

书中用了某成员被攻击, 同时通知其他所有成员的例子.这就是典型的对象联动.

//典型的订阅行为,有改变可以通过notifyAll通知所有订阅者
Subject::notifyAll() {

    for(auto i : this->members) {
        i->notify(this->status);
    }
}

//核心就是参数传进来的sub,实现对象联动
Observer::beAttacked(Subject * sub) {
    sub->notifyAll();
}

subject 可以添加 observer,反过来,用一个 observer 也应该可以添加不同的 subject.

MVC也是典型的观测者模式设计. 2018-08-13-11-22-29

用一个典型的文章订阅作为例子:

#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)