观察者模式与推拉模型

作者卡奴达摩

连接:http://blog.csdn.net/zhengzhb/article/details/7471978


定义:定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。

类型:行为类模式

类图:

        在软件系统中经常会有这样的需求:如果一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的变化。比如,我们要设计一个右键菜单的功能,只要在软件的有效区域内点击鼠标右键,就会弹出一个菜单;再比如,我们要设计一个自动部署的功能,就像eclipse开发时,只要修改了文件,eclipse就会自动将修改的文件部署到服务器中。这两个功能有一个相似的地方,那就是一个对象要时刻监听着另一个对象,只要它的状态一发生改变,自己随之要做出相应的行动。其实,能够实现这一点的方案很多,但是,无疑使用观察者模式是一个主流的选择。

观察者模式的结构

在最基础的观察者模式中,包括以下四个角色:

  • 被观察者:从类图中可以看到,类中有一个用来存放观察者对象的Vector容器(之所以使用Vector而不使用List,是因为多线程操作时,Vector在是安全的,而List则是不安全的),这个Vector容器是被观察者类的核心,另外还有三个方法:attach方法是向这个容器中添加观察者对象;detach方法是从容器中移除观察者对象;notify方法是依次调用观察者对象的对应方法。这个角色可以是接口,也可以是抽象类或者具体的类,因为很多情况下会与其他的模式混用,所以使用抽象类的情况比较多。
  • 观察者:观察者角色一般是一个接口,它只有一个update方法,在被观察者状态发生变化时,这个方法就会被触发调用。
  • 具体的被观察者:使用这个角色是为了便于扩展,可以在此角色中定义具体的业务逻辑。
  • 具体的观察者:观察者接口的具体实现,在这个角色中,将定义被观察者对象状态发生变化时所要处理的逻辑。

观察者模式代码实现

abstract class Subject {  
    private Vector<Observer> obs = new Vector<Observer>();  
      
    public void addObserver(Observer obs){  
        this.obs.add(obs);  
    }  
    public void delObserver(Observer obs){  
        this.obs.remove(obs);  
    }  
    protected void notifyObserver(){  
        for(Observer o: obs){  
            o.update();  
        }  
    }  
    public abstract void doSomething();  
}  
  
class ConcreteSubject extends Subject {  
    public void doSomething(){  
        System.out.println("被观察者事件反生");  
        this.notifyObserver();  
    }  
}  
interface Observer {  
    public void update();  
}  
class ConcreteObserver1 implements Observer {  
    public void update() {  
        System.out.println("观察者1收到信息,并进行处理。");  
    }  
}  
class ConcreteObserver2 implements Observer {  
    public void update() {  
        System.out.println("观察者2收到信息,并进行处理。");  
    }  
}  
  
public class Client {  
    public static void main(String[] args){  
        Subject sub = new ConcreteSubject();  
        sub.addObserver(new ConcreteObserver1()); //添加观察者1  
        sub.addObserver(new ConcreteObserver2()); //添加观察者2  
        sub.doSomething();  
    }  
}  


运行结果

被观察者事件反生

观察者1收到信息,并进行处理。

观察者2收到信息,并进行处理。

        通过运行结果可以看到,我们只调用了Subject的方法,但同时两个观察者的相关方法都被同时调用了。仔细看一下代码,其实很简单,无非就是在Subject类中关联一下Observer类,并且在doSomething方法中遍历一下Observer的update方法就行了。

观察者模式的优点

        观察者与被观察者之间是属于轻度的关联关系,并且是抽象耦合的,这样,对于两者来说都比较容易进行扩展。

        观察者模式是一种常用的触发机制,它形成一条触发链,依次对各个观察者的方法进行处理。但同时,这也算是观察者模式一个缺点,由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。并且,在链式结构中,比较容易出现循环引用的错误,造成系统假死。

 

总结

       Java语言中,有一个接口Observer,以及它的实现类Observable,对观察者角色常进行了实现。我们可以在jdk的api文档具体查看这两个类的使用方法。

       做过VC++、JavaScript DOM或者AWT开发的朋友都对它们的事件处理感到神奇,了解了观察者模式,就对事件处理机制的原理有了一定的了解了。如果要设计一个事件触发处理机制的功能,使用观察者模式是一个不错的选择,AWT中的事件处理DEM(委派事件模型Delegation Event Model)就是使用观察者模式实现的。


作者:RayChase

连接:http://raychase.iteye.com/blog/1337015


观察者模式,指的是定义一种对象间的一对多的关系,当一个对象的状态发生变化的时候,所有依赖于它的对象都将得到通知并更新自己。


 

现在要说的分歧在这里:

“推”的方式是指,Subject维护一份观察者的列表,每当有更新发生,Subject会把更新消息主动推送到各个Observer去。

“拉”的方式是指,各个Observer维护各自所关心的Subject列表,自行决定在合适的时间去Subject获取相应的更新数据。

 

“推”的好处包括:

1、高效。如果没有更新发生,不会有任何更新消息推送的动作,即每次消息推送都发生在确确实实的更新事件之后,都是有意义的。

2、实时。事件发生后的第一时间即可触发通知操作。

3、可以由Subject确立通知的时间,可以避开一些繁忙时间。

4、可以表达出不同事件发生的先后顺序。

 

“拉”的好处包括:

1、如果观察者众多,Subject来维护订阅者的列表,可能困难,或者臃肿,把订阅关系解脱到Observer去完成。

2、Observer可以不理会它不关心的变更事件,只需要去获取自己感兴趣的事件即可。

3、Observer可以自行决定获取更新事件的时间。

4、拉的形式可以让Subject更好地控制各个Observer每次查询更新的访问权限。

 

----------------------------------------------------------------------------------------------------------------------------------

2012-2-27 补充:

事实上“推”和“拉”可以比较的内容太多了,比如:

客户端通常是不稳定的,服务端是稳定的,如果消息由客户端主动发起去获取,它很容易找到服务端的地址,可以比较容易地做到权限控制(集中在服务端一处),服务端也可以比较容易地跟踪客户端的位置和状态,反之则不行;

互联网页面的访问就是一个最好的“拉”的模式的例子;

通常我们希望把压力分散到各个客户端上去,服务端只做最核心的事情,只提供内容,不管理分发列表;

……

还有一个idea是关于“推”和“拉”结合的形式,例如,服务端只负责通知某一些数据已经准备好,至于是否需要获取和什么时候客户端来获取这些数据,完全由客户端自行确定。


、、、、、、、转者、、、、、、、

#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <algorithm>

/**********************************************************************************
最开始使用智能指针实现的,但是发现了一些"问题":
1、智能指针没有复制兼容性原则:指向子类的智能指针不能向指向父类的智能指针赋值/初始化
2、指向基类的智能指针甚至不能用来指向原生的子类对象
除非自己实现转换函数,且转换函数的频繁调用成为必然,但实际上不应该频繁的转换类型

一些注意事项,观察者在自身析构之前,必须向主题取消注册,以免产生不必要的麻烦
***********************************************************************************/
class AbsSubject;				//前向声明

class AbsObserver
{
public:
	virtual void Update(std::string report) = 0;		//推送内容方式,注意 主题与观察者是分离的,不要盲目追求效率而声明参数为引用
	virtual void Updata(AbsSubject *sj) = 0;			//拉取内容方式,想要知道具体主题的具体内容,需自己去取感兴趣的项,此方式需求客户遵守约定,并且不在运行时修改成员变量
	virtual ~AbsObserver(){};
};

class AbsSubject
{
public:
	virtual void RegisterObserver(AbsObserver *ob) = 0;	//由于允许子类自行选取数据结构存储观察者,因此定义为纯虚函数,此外,此处也助于减少行为的继承
	virtual void CancelObserver(AbsObserver *ob) = 0;
	virtual void Notify() = 0;
	virtual ~AbsSubject(){};							//AbsSubject的子类中可能拥有指向观察者的指针,但不应该在析构函数中释放这些指针,毕竟主题与观察者是独立的
};

class Secretary : public AbsSubject		//专用秘书,单人使用
{
private:
	AbsObserver *pLeader;
	std::string plan;

	void Notify()
	{
		if( NULL != pLeader )
		{
			pLeader->Update(plan);
		}
	}

public:
	Secretary() : pLeader(NULL)
	{	
	}
	explicit Secretary(AbsObserver *ob)
	{
		RegisterObserver(std::move(ob));		
	}
	void RegisterObserver(AbsObserver *ob)
	{
		pLeader = ob;
	}
	void CancelObserver(AbsObserver *ob)
	{
		pLeader = NULL;
	}
	void SetPlan(std::string NewPlan)	
	{
		plan = NewPlan;
		Notify();
	}
};

class Reception : public AbsSubject		//勤劳的前台MM
{
private:
	std::vector<AbsObserver *> member;
	std::string bossEvent;
	std::string managerEvent;
	std::string HREvent;

public:
	Reception()
	{
	}
	explicit Reception(AbsObserver *ob)
	{
		RegisterObserver(ob);
	}
	void RegisterObserver(AbsObserver *ob)
	{
		member.push_back(std::move(ob));		//避免拷贝
	}
	void CancelObserver(AbsObserver *ob)
	{
		std::vector<AbsObserver *>::const_iterator it;
		it = std::find(member.cbegin(), member.cend(), ob);
		if( it != member.cend() )
		{
			member.erase(it);
		}
	}
	void Notify()
	{
		for (auto &it : member)
		{
			if( NULL != it )
			{
				it->Updata(this);
			}
		}
	}
	void HasEvent(std::string bossEvent, std::string managerEvent, std::string HREvent)
	{
		this->bossEvent = bossEvent;
		this->managerEvent = managerEvent;
		this->HREvent = HREvent;
		Notify();
	}
	std::string GetBossEvent()const
	{
		return bossEvent;
	}
	std::string GetMnagerEvent()const
	{
		return managerEvent;
	}
	std::string GetHREvent()const
	{
		return HREvent;
	}
};

class Boss : public  AbsObserver			
{
private:
	std::string plan;
public:
	void Update(std::string report)								//推送数据,推送方式下观察者不能选择性获取自己感兴趣的内容
	{
		plan = report;
		std::cout << "Boss " << report << std::endl;
	}
	void Updata(AbsSubject * sj)									//拉取数据,此方式可由观察者选择自己感兴趣的内容
	{
		if(  Reception * re = dynamic_cast<Reception *>(sj) )
		{			
			std::cout << "Boss " << re->GetBossEvent() << std::endl;		//挑自己感兴趣的
		}
	}
};

class Manager : public AbsObserver			
{
public:
	void Update(std::string report)
	{
		std::cout << report << std::endl;
	}
	void Updata(AbsSubject * sj)
	{
		if (Reception * re = dynamic_cast<Reception *>(sj))
		{
			std::cout << "Manager " << re->GetMnagerEvent() << std::endl;
		}
	}
	void DoSomething()
	{
		std::cout << "DoSomething" << std::endl;
	}
};

class HR : public AbsObserver					
{
public:
	void Update(std::string report)
	{
		std::cout << report << std::endl;
	}
	void Updata(AbsSubject * sj)
	{
		if( Reception * re = dynamic_cast<Reception *>(sj) )
		{
			std::cout << "HR " <<re->GetHREvent() << std::endl;
		}
	}
	void DoSomething()
	{
		std::cout << "DoSomething" << std::endl;
	}
};

int main(int argc, char **argv)
{
	Boss boss;
	Secretary secretary(&boss);
	Reception *reception = new Reception;
	if( NULL == reception )
		return -1;

	Manager manager;
	HR *hr = new HR;
	if( NULL == hr )
		return -1;

	reception->RegisterObserver(&manager);
	reception->RegisterObserver(hr);
	reception->RegisterObserver(&boss);

	hr->DoSomething();

	secretary.SetPlan(std::string("phone call"));
	reception->HasEvent(std::string("visit"), std::string("expressage"), std::string("interview"));

	manager.DoSomething();

	secretary.CancelObserver(&boss);
	reception->CancelObserver(hr);
	reception->CancelObserver(&manager);
	reception->CancelObserver(&boss);
	
	secretary.SetPlan(std::string("nothing"));
	reception->HasEvent(std::string("nothing"), std::string("nothing"), std::string("nothing"));

	delete reception;
	delete hr;

	return 0;
}





  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
观察者模式是一种行为设计模式,用于在对象之间建立一对多的依赖关系,当一个对象的状态发生变化时,它的所有依赖对象都会收到通知并自动更新。观察者模式可以通过推模型和拉模型来实现。 推模型是指主题对象在状态变化时主动向观察者推送详细信息。在推模型中,主题对象通常会将自身的状态作为参数传递给观察者的更新方法。这样,观察者可以直接获取到主题对象的详细信息,并进行相应的处理。 拉模型是指主题对象在状态变化时只向观察者发送简单的通知,观察者需要自己主动去获取主题对象的详细信息。在拉模型中,主题对象通常只会将自身的引用传递给观察者的更新方法。观察者需要通过主题对象的引用来获取所需的信息。 区别: 1. 数据传递方式不同:推模型中,主题对象主动向观察者传递详细信息;拉模型中,主题对象只传递简单的通知,观察者需要自己获取详细信息。 2. 信息获取方式不同:推模型中,观察者可以直接获取到主题对象的详细信息;拉模型中,观察者需要通过主题对象的引用来获取所需的信息。 3. 灵活性不同:推模型中,主题对象需要知道观察者的具体细节,因此主题对象与观察者之间的耦合度较高;拉模型中,主题对象只需要提供一个接口给观察者使用,观察者可以根据自身需要选择获取哪些信息,因此主题对象与观察者之间的耦合度较低。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值