观察者模式
##来自博客 单例,工厂,观察者,我认为是设计模式三杰,为何这么说,因为这三种设计模式使用频率最高,变化最多,而且覆盖面最广,当然,也是面试中最容易被问到的。当然,像装饰,建造,策略这几个也是经常会用到的,后面文章中我会一一展开讨论。
从这里开始,我们在介绍设计模式之前,需要强调一下角色。实际上从上篇工厂模式中,就已经有角色的概念。大家想想,工厂模式中有哪些角色?没错,就是工厂角色和产品角色。同样,在观察者模式中,也有两个角色,就是观察者和被观察者。在理解设计模式的时候,首先要有个概念,就是每个角色都对应这一个类,比如观察者模式,观察者肯定对应着一个观察者类,被观察者肯定对应的被观察者类。那么设计模式实际上就是通过面向对象的特性,将这些角色解耦。
观察者模式本质上就是一种订阅/发布的模型,从逻辑上来说就是一对多的依赖关系。什么意思呢?好比是一群守卫盯着一个囚犯,只要囚犯一有异动,守卫就必须马上采取行动(也有可能是更新状态,本质上也是一种行动),那么守卫就是观察者,囚犯就是被观察者。
在一个系统中,实现这种一对多的而且之间有一定关联的逻辑的时候,由于需要保持他们之间的协同关系,所以最简便的方法是采用紧耦合,把这些对象绑定到一起。但是这样一来,一旦有扩展或者修改的时候,开发人员所面对的难度非常大,而且很容易造成Bug。那么观察者模式就解决了这么一个问题,在保持一系列观察者和被观察者对象协同工作的同时,把之间解耦了。
好了,废话不多,撸代码:
//被观察者
public interface IObject {
IList<IMonitor> ListMonitor { get; set; } //定义观察者集合,因为多个观察者观察一个对象,所以这里用集合
string SubjectState { get; set; } //被观察者的状态
void AddMonitor(IMonitor monitor); //添加一个观察者
void RemoveMonitor(IMonitor monitor); //移除一个观察者
void SendMessage(); //向所有观察者发送消息
}
public class Subject : IObject
{
private IList<IMonitor> listMonitor = new List<IMonitor>();
public string SubjectState //被观察者的状态
{
get;set;
}
public IList<IMonitor> ListMonitor //实现具体的观察者列表属性
{
get { return listMonitor; }
set { listMonitor = value; }
}
public void AddMonitor(IMonitor monitor) //实现具体的添加观察者方法
{
listMonitor.Add(monitor);
}
public void RemoveMonitor(IMonitor monitor) //实现具体的移除观察者方法
{
listMonitor.Remove(monitor);
}
public void SendMessage() //实现具体的发送消息方法
{
foreach (IMonitor m in listMonitor) //发送给所有添加过的观察者,让观察者执行update方法以同步更新自身状态
{
m.Update();
}
}
}
//观察者
public interface IMonitor //定义观察者接口 { void Update(); }
public class Monitor : IMonitor //实现具体观察者
{
private string monitorState="Stop!"; //观察者初始状态,会随着被观察者变化而变化
private string name; //观察者名称,用于标记不同观察者
private IObject subject; //被观察者对象
public Monitor (IObject subject, string name) //在构造观察者时,传入被观察者对象,以及标识该观察者名称
{
this.subject = subject;
this.name = name;
Console.WriteLine("我是观察者{0},我的初始状态是{1}", name, monitorState);
}
public void Update() //当被观察者状态改变,观察者需要随之改变
{
monitorState = subject.SubjectState;
Console.WriteLine("我是观察者{0},我的状态是{1}", name, monitorState);
}
}
//前端调用
static void Main(string[] args) {
IObject subject = new Subject();
subject.AddMonitor(new Monitor(subject, "Monitor_1"));
subject.AddMonitor(new Monitor(subject, "Monitor_2"));
subject.AddMonitor(new Monitor(subject, "Monitor_3"));
subject.SubjectState = "Start!";
subject.SendMessage();
Console.Read();
}
}
结果如下:
我是观察者Monitor_1,我的初始状态是Stop!
我是观察者Monitor_2,我的初始状态是Stop!
我是观察者Monitor_3,我的初始状态是Stop!
我是观察者Monitor_1,我的状态是Start!
我是观察者Monitor_2,我的状态是Start!
我是观察者Monitor_3,我的状态是Start!
这样就完成了一个观察者模式。我们回过头来看看,在被观察者中,我定义了一个集合用来存放观察者,并且我写了一个Add方法一个Remove方法来添加和移除观察者,这体现了一对多的关系,也提供了可以控制观察者的方式。所以,我们得到第一个关键点:每个观察者需要被保存到被观察者的集合中,并且被观察者提供添加和删除的方式。
然后我么再看一下,观察者和被观察者之间的交互活动。不难发现,我是在添加一个观察者的时候,把被观察者对象以构造函数的形式给传入了观察者。最后我让被观察者执行sendmessage方法,这时会触法所有观察着的update方法以更新状态。所以我们得到第二个关键点,被观察者把自己传给观察者,当状态改变后,通过遍历或循环的方式逐个通知列表中的观察者。
好了,到这里你应该可以把握住观察者模式的关键了。但这里有个问题,被观察者是通过构造函数参数的形式,传给观察者的,而观察者对象时被Add到被观察者的List中。所以,我们得到第三个关键点,虽然解耦了观察者和被观察者的依赖,让各自的变化不大影响另一方的变化,但是这种解耦并不是很彻底,没有完全解除两者之间的耦合。
有很多同学比较怕被问到观察者模式,特别是搞.Net的同学。为什么呢?因为一旦涉及到观察者模式,必然会涉及到2中类型,委托和事件。因为很多人并不理解委托和事件,或者只停留在潜层次。那么,委托,事件,和观察者模式到底有什么关系呢?
首先我们来看委托。委托说白了,就是可以把方法当做另一个方法参数来传递的东东,当然方法签名需要注意一下。委托可以看做是方法的抽象,也就是方法的“类”,一个委托的实例可以是一个或者多个方法。我们可以通过+=或者-=把方法绑定到委托或者从委托移除。
再来看事件,实际上事件是一种特殊的委托。怎么说呢,首先事件也是委托,只是在声明事件的时候,需要加上event,如果你用reflector去看一个事件,你会发现里面就3样东西,一个Add_xxxx方法,一个Remove_xxx方法,一个委托。说道这里,是不是觉得和上面我们定义被观察者时的Add方法,Remove方法有些联系?
没错,实际上.Net的事件机制就是观察者模式的一种体现,并且是利用委托来实现。本质上事件就是一种订阅-发布模型也就是观察者模式,这种机制中包含2个角色,一个是发布者,一个是订阅者。发布者类也就类似于被观察者,发布者类包含事件和委托定义,以及其之间的关系,发布者类的对象调用事件通知其他订阅者。而订阅者类也就类似于观察者,观察者接受事件,并且提供处理的逻辑。
也就是说,订阅者对象(观察者)中的方法会绑定到发布者(被观察者)对象的委托中,一旦发布者(被观察者)中事件被调用,发布者(被观察者)就会调用委托中绑定的订阅者(观察者)的处理逻辑或者说是处理程序,这就是通过观察者模式实现的事件,虽然这段话比较拗口,但是我想理解起来应该还是不太难把。。。
好了,你虽然现在已经对面试官逼逼叨了上面一大通,但是贱贱的面试官仍然想challenge你一下,否则不就是太没面子么。。。
你刚刚说了,在普通的观察者模式中,解耦并不彻底,那么在事件的发布订阅模型中,解耦彻底吗?为什么? 答案是肯定的。因为在事件中,订阅者和发布者之间是通过把事件处理程序绑定到委托,并不是把自身传给对方。所以解决了观察者模式中不完全解耦的问题。这也是关键点之四
有的,通过+=去把方法绑定到委托,很容易忘记-=。如果只绑定不移除,这个方法会一直被引用。我们知道GC去回收的时候,只会处理没有被引用的对象,只要是还被引用的对象时不会被回收掉的。所以如果在长期不关闭的系统中(比如监控系统),大量的代码使用+=而不-=,运行时间长以后有可能会内存溢出。
这个上面已经提到了,委托时一种类型,事件是一种特殊的委托,观察者模式是一种设计模式,事件的机制是观察者模式的一种实现,其中订阅者和发布者通过委托实现协同工作。
好的,最后我们还是来归纳一下观察者模式的关键点
一、每个观察者需要被保存到被观察者的集合中,并且被观察者提供添加和删除的方式。
二、被观察者把自己传给观察者,当状态改变后,通过遍历或循环的方式逐个通知列表中的观察者。
三、虽然解耦了观察者和被观察者的依赖,让各自的变化不大影响另一方的变化,但是这种解耦并不是很彻底,没有完全解除两者之间的耦合。
四、在事件中,订阅者和发布者之间是通过把事件处理程序绑定到委托,并不是把自身传给对方。所以解决了观察者模式中不完全解耦的问题
注意点:在使用委托绑定方法时,需要注意移除方法,否则可能会造成内存溢出。
建议:不能理解事件和委托的同学,好好地把事件和委托看一看,并且自己写点代码加深印象。
##来自我的经验案例:
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
using namespace std;
template< class T > class CListener;
template< class T >
class CNotifyProcessor
{
public:
CNotifyProcessor(void){}
virtual ~CNotifyProcessor(void){}
/************************************
>@ Method: Notify
>@ FullName: CNotifyProcessor::Notify
>@ Brief: 接收通知数据接口
>@ Access: virtual public
>@ Returns: void
>@ Qualifier:
>@ Parameter: const NotifyData & notifyData
************************************/
virtual void Notify(const T& notifyData) = 0;
bool RegisterListener(CListener& listener)
{
return listener.RegisterProcessor(this);
}
bool UnRegisterListener(CListener& listener)
{
return listener.UnRegisterProcessor(this);
}
protected:
CNotifyProcessor(const CNotifyProcessor&);
CNotifyProcessor& operator = (const CNotifyProcessor&);
};
//>群发方式,目前只会有一个处理者,先释放处理者,再释放监听者
template< class T >
class CListener
{
public:
CListener(void){}
virtual ~CListener(void)
{
try
{
ClearProcessor();
}
catch(...)
{
}
}
/************************************
>@ Method: RegisterProcessor
>@ FullName: CListener::RegisterProcessor
>@ Brief: 添加处理者
>@ Access: virtual public
>@ Returns: bool
>@ Qualifier:
>@ Parameter: CNotifyProcessor * notifyProcessor
************************************/
virtual bool RegisterProcessor(CNotifyProcessor<T>* notifyProcessor)
{
if (NULL == notifyProcessor)
{
return false;
}
HPR_Guard guard(&m_notifyProcessorMutex);
map<int,CNotifyProcessor<T>*>::iterator iter = m_notifyProcessor.find((int)notifyProcessor);
if (iter == m_notifyProcessor.end())
{
m_notifyProcessor.insert(make_pair<int,CNotifyProcessor<T>*>((int)notifyProcessor,notifyProcessor));
return true;
}
return false;
}
/************************************
>@ Method: UnRegisterProcessor
>@ FullName: CListener::UnRegisterProcessor
>@ Brief: 删除处理者
>@ Access: virtual public
>@ Returns: bool
>@ Qualifier:
>@ Parameter: CNotifyProcessor * notifyProcessor
************************************/
virtual bool UnRegisterProcessor(CNotifyProcessor<T>* notifyProcessor)
{
if (NULL == notifyProcessor)
{
return false;
}
HPR_Guard guard(&m_notifyProcessorMutex);
map<int,CNotifyProcessor<T>*>::iterator iter = m_notifyProcessor.find((int)notifyProcessor);
if (iter != m_notifyProcessor.end())
{
m_notifyProcessor.erase(iter);
return true;
}
return false;
}
/************************************
>@ Method: Notify
>@ FullName: CListener::Notify
>@ Brief: 下发监听者收到的通知(群发方式)
>@ Access: virtual public
>@ Returns: void
>@ Qualifier:
>@ Parameter: const NotifyData& notifyData
************************************/
virtual void NotifyProcessor(const T& notifyData)
{
HPR_Guard guard(&m_notifyProcessorMutex);
for (map<int,CNotifyProcessor<T>*>::iterator iter = m_notifyProcessor.begin();iter != m_notifyProcessor.end();iter++)
{
iter->second->Notify(notifyData);
}
}
/************************************
>@ Method: ClearProcessor
>@ FullName: CListener::ClearProcessor
>@ Brief:
>@ Access: public
>@ Returns: void
>@ Qualifier:
************************************/
void ClearProcessor()
{
HPR_Guard guard(&m_notifyProcessorMutex);
m_notifyProcessor.clear();
}
protected: CListener(const CListener& ); CListener& operator = (const CListener& ); private: map<int,CNotifyProcessor*> m_notifyProcessor; HPR_Mutex m_notifyProcessorMutex; };
观察者模式所做的工作其实就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
最后我们还是来归纳一下观察者模式的关键点:
class Observer;
//Subject,抽象通知者或者主题
class Subject
{
protected:
std::string SubjectState;
public:
virtual void Attach(Observer* observer)=0;
virtual void Detach(Observer* observer)=0;
virtual void Notify()=0;
std::string GetSubjectState();
void SetSubjectState(std::string state);
};
//ConcreteSubject,具体通知者或者具体主题。
class Boss:public Subject
{
private:
std::list<Observer> observers;
std::string action;
public:
void Attach(Observer observer);
void Detach(Observer* observer);
void Notify();
};
std::string Subject::GetSubjectState()
{
return SubjectState;
}
void Subject::SetSubjectState(std::string state)
{
this->SubjectState=state;
}
void Boss::Attach(Observer* observer)
{
observers.push_back(observer);
}
void Boss::Detach(Observer* observer)
{
std::list<Observer>::iterator it;
for(it=observers.begin();it!=observers.end();it++)
{
if(it==observer)
{
observers.erase(it);
break;
}
}
}
void Boss::Notify()
{
std::list<Observer>::iterator it;
for(it=observers.begin();it!=observers.end();it++)
{
(*it).Update();
}
}
//Observer,抽象观察者
class Observer
{
protected:
std::string name;
Subject* sub;
public:
Observer();
Observer(std::string name,Subject* sub);
virtual void Update();
bool operator==(const Observer&)const;
};
//ConcreteObserver,具体观察者,股票观察者
class StockObserver:public Observer
{
public:
StockObserver();
StockObserver(std::string name,Subject* sub);
void Update();
};
//ConcreteObserver,具体观察者,NBA观察者
class NBAObserver:public Observer
{
public:
NBAObserver();
NBAObserver(std::string name,Subject* sub);
void Update();
};
Observer::Observer(){}
Observer::Observer(std::string name,Subject* sub)
{
this->name=name;
this->sub=sub;
}
void Observer::Update()
{
std::cout<<"Observer.Update()"<<std::endl;
}
bool Observer::operator(const Observer& observer)const
{
return (this->nameobserver.name)&&(this->sub==observer.sub);
}
StockObserver::StockObserver(){}
StockObserver::StockObserver(std::string name,Subject* sub)
{
this->name=name;
this->sub=sub;
}
void StockObserver::Update()
{
std::cout<<sub->GetSubjectState()<<" "<<name<<" "<<"关闭股市行情,继续工作!"<<std::endl;
}
NBAObserver::NBAObserver(){}
NBAObserver::NBAObserver(std::string name,Subject* sub)
{
this->name=name;
this->sub=sub;
}
void NBAObserver::Update()
{
std::cout<<sub->GetSubjectState()<<" "<<name<<" "<<"关闭NBA直播,继续工作!"<<std::endl;
}
demo:
void main()
{
//通知者
Subject* huhansan=new Boss();
//4个观察者实例
Observer* tongshi1=new StockObserver("魏关姹",huhansan);
Observer* tongshi2=new StockObserver("易管察",huhansan);
Observer* tongshi3=new NBAObserver("霍华德",huhansan);
Observer* tongshi4=new NBAObserver("林书豪",huhansan);
//将4个观察者都加入到通知者的通知队列中
huhansan->Attach(tongshi1);
huhansan->Attach(tongshi2);
huhansan->Attach(tongshi3);
huhansan->Attach(tongshi4);
//魏关姹没有被老板通知到,减去。
huhansan->Detach(tongshi1);
huhansan->SetSubjectState("我胡汉三回来了!");
//通知
huhansan->Notify();
delete huhansan;
delete tongshi1;
delete tongshi2;
delete tongshi3;
delete tongshi4;
std::cout<<""<<std::endl;
system("pause");
}
这个例子中,subject是作为主题的基类去工作的,具体的主题还是需要再去派生出来,同样的,观察者也是这样,需要派生出来。这样做,确实不如上面利用虚基类来处理得巧妙,后续尽量使用上面虚基类的实现方式吧。