现在的位置: 首页 > 综合 > 正文

【2013.2.15】来吧,互粉吧——Observer(使用List)

2018年02月19日 ⁄ 综合 ⁄ 共 3553字 ⁄ 字号 评论关闭

// // // // // // // // //

///2013.2.15

// // // // // // // // //

如果要给设计模式按照难易程度评级别的话,

之前的设计模式大多是一星或一星半(当然,也有部分原因是我没有深入)。

但是今天要讲的这个设计模式,

难度应该是三星(Not samsung :-P)。

但与此相比,

其实用性却是五星级别的。

因为无所不在的MVC结构(Model/View/Control),

就是这个设计模式最好的实例。

当然,

因为本文的面向对象是初学者,

故笔者在这里不打算使用MVC作为讲解。

如果想要了解更多关于MVC,请戳维基百科

除此之外,

这个设计模式还有一个别名——发布/订阅模式。

运气不错,

我之前学习的《C#3.0设计模式》一书中就是使用这个例子来进行模式拆分讲解的:

最简单的形容就是网页中的RSS订阅了,

不过话说在这个时代这种订阅模式已经不再风靡,

取而代之的是微博2.0时代。

众所周知,

微博中我们每个人都是博主的同时也是粉丝。

我们可以关注别人,

也可以被关注。

从粉丝角度来讲,

每一个粉丝都是一个观察者(Observer),

他们都会订阅(关注)你(的微博),

当他们关注你的时候,

你将他们放到一个粉丝列表之中——我们称之为Attach,

然后在这个粉丝列表中,

每一次你发一条新微博(更新一次状态),

在这个粉丝列表中的人都将接收到这条通知(显示在微博消息中),

这个过程我们称之为Notify,

然而当他发现你有时候会刷屏的时候,

会很生气,

于是决定将你取消关注——我们称之为Detach。

于是他也将从你的粉丝列表中消失,

这样当你再次发送新微博的时候,

他将不再接收。

【核心】订阅者订阅发布者的发布(请连续念三遍,是不是会不经意间说出布丁布丁?;-P)。

UML图:

不得不讲解一下这张UML图:

首先是请大家注意Observer左边的那个小星星,

代表Subject类中包含Observer集合(数个对象)。

Subject的三个接口及其对应关系如下:

Attach——新增观察者到Observer集合中。

Detach——将已存在观察者移除。

Notify——利用Observer对象的Update来通知本Subject已经发生变化。

ConcreteSubject类中的Set/GetState用于设置/获取此Subject的状态。

除此之外,

为什么会有两个ConcreteObserver(A/B)呢?

因为这是为了表明此模式适用于Subject具有多个观察者类别的情形。

比如说你的粉丝A与粉丝B不可能是同一个账号吧?

这样每个ConcreteObserver都会拥有自己的Update。

示例代码:

【大致思路】

FansA与FansB都关注Jay,当Jay发送状态时,他们的微博界面都会显示更新。

Subject.h

#ifndef _SUBJECT_H_
#define _SUBJECT_H_

#include<list>
#include<string>

class Observer;

class Subject
{
public:
	Subject();
	~Subject(){}

	void Attach(Observer* obj);
	void Detach(Observer* obj);
	void Notify();

	virtual std::string getState() = 0;
	virtual void setState(const std::string& s) = 0;

private:
	std::list<Observer*>* fansList;
};

class ConcreteSubject:public Subject
{
public:
	ConcreteSubject();
	~ConcreteSubject(){}

	virtual std::string getState();
	virtual void setState(const std::string& s);

private:
	std::string state;
};


#endif

Subject.cpp

#include"Subject.h"
#include"Observer.h"
#include<string>
#include<list>

using namespace std;

Subject::Subject()
{
	fansList = new list<Observer*>;
}

void Subject::Attach(Observer* obj)
{
	//Add to list.
	fansList->push_back(obj);
}

void Subject::Detach(Observer* obj)
{
	//Remove from list.
	if(obj != nullptr)
		fansList->remove(obj);
}

void Subject::Notify()
{
	list<Observer*>::iterator iter;

	//Notify everyone on the list.
	iter = fansList->begin();
	for(;iter != fansList->end();iter++)
	{
		(*iter)->Update(this);
	}
}

ConcreteSubject::ConcreteSubject()
{
	state = "";
}

string ConcreteSubject::getState()
{
	return state;
}

void ConcreteSubject::setState(const string& s)
{
	state = s;
}

Observer.h

#ifndef _OBSERVER_H_
#define _OBSERVER_H_

#include<string>
#include"Subject.h"

class Observer
{
public:
	Observer(){}
	~Observer(){}
	virtual void Update(Subject* s);

protected:
	std::string state;
	Subject* subject;
};

class FansA: public Observer
{
public:
	FansA(Subject* s);
	~FansA();
	virtual void Update(Subject* s);
};

class FansB: public Observer
{
public:
	FansB(Subject* s);
	~FansB();
	virtual void Update(Subject* s);
};


#endif

Observer.cpp

#include"Observer.h"
#include<iostream>

using namespace std;

void Observer::Update(Subject* s)
{
	cout<<"Got a new Message:"<<endl;
}

FansA::FansA(Subject* s)
{
	s->Attach(this);
	subject = s;
}

FansA::~FansA()
{
	subject->Detach(this);

	if(subject != nullptr)
		delete subject;
}

void FansA::Update(Subject* s)
{
	cout<<"FansA ";
	Observer::Update(s);

	cout<<s->getState()<<endl;
	cout<<""<<endl;
}

FansB::FansB(Subject* s)
{
	s->Attach(this);
	subject = s;
}

FansB::~FansB()
{
	subject->Detach(this);

	if(subject != nullptr)
		delete subject;
}

void FansB::Update(Subject* s)
{
	cout<<"FansB ";
	Observer::Update(s);

	cout<<s->getState()<<endl;
	cout<<""<<endl;
}

main.cpp

#include"Observer.h"

int main()
{
	Subject* Jay = new ConcreteSubject();

	Observer* fansA = new FansA(Jay);
	Observer* fansB = new FansB(Jay);

	Jay->setState("Happy new year,everyone,I'm Super Jay");

	Jay->Notify();

	return 0;
}

输出结果:



注意事项:

代码使用list来作为存储容器,

同样也可以使用其他类型的集合来装载。

需要注意的是,

在Subject的构造函数中一定要先对这个list进行初始化才能使用。

还有在Observer的析构函数中,

可以看到要先进行detech然后再删除,

这样就避免了内存分配问题。


抱歉!评论已关闭.