定义:观察者模式-在对象之间定义一对多的依赖。这样以来,一个对象改变状态时,依赖它的对象都会收到通知,并自动更新。
OO设计的的思想里,我们强调,我们的设计总是在为对象之间的交互的松耦合而努力。观察者模式,正是在一对多的依赖中,寻找一种松耦合的实现方式。我们在日常生活中,经常会遇到这种情况,就是某个对象拥有某些状态,而其它对象都需要使用这些状态,从而执行某些行为。比如,在一个数据控制中心,有一个装置来负责采集数据,并把数据实时的告知其它的部分,这些部门需要利用这些数据。因此,这就是一个一对多的依赖关系。假设我们以卫星控制系统来描述这个模式。
假设系统的行为为:有一个数据采集中心,负责获取卫星的实时数据:经度,纬度,飞行时常。
数据采集中西负责把及时采集到的数据通知其它部门,假设有两个部门,一个负责实时显示卫星的当前状态,另外一个部门,负责通过接收到的数据进行计算,并输出下一步的控制信号。并且,该系统还不够完善,可能会增加新的部门,该部门也需要利用卫星的实时数据,做其它的工作。
根据以上的系统需求,我们可以知道,这是一个一对多的关系,并且,需要系统有很好的扩展性,即系统对象之间的耦合性要小。那我们怎么来实现呢?
系统有一个数据采集中心,我们需要来设计一个类来描述这个数据采集中心,假设叫做SatelliteData,
那么显示部门也用一个类DisplayBoard类以及计算部门也用一个类Calculate来描述。类SatelliteData定义如下所示:
class SatelliteData
{
float longitude;
float latitude;
float timeLast;
public float getLongitude()
{
……
}
public float getLatitude()
{
……
}
public float getTimeLast()
{
……
}
public setChange()
{
longitude=getLongitude();
latitude=getLatitude();
timeLast=getTimeLast();
displayBoard.update(longitudu,latitude,timeLast);
calculate.update(longitudu,latitude,timeLast);
}
}
其中displayBoard是类DisplayBoard的对象,calculate是Calculate的对象。我们来看看这段代码,实际上违背了我们在策略模式中一再强调的面向接口编程的思想,红色部分的代码是面向实现的,针对一个个的具体需要用到这些状态的对象,发送通知。这样,如果再增加新的使用对象,那就需要修改SatelliteDate类的代码,并且,这样造成的耦合性很大。
那我们用观察者模式如何来解决这个问题呢?观察者模式定义了一对多的依赖,其中“一”被称作主题,而“多”被称作观察者。观察者通过注册的方式来订阅主题,当不需要的时候,再通过主题取消订阅。这样,多个观察者通过订阅的方式共享主题里的内容。当主题内容改变的时候,需要动态的通知观察者,以便他们即使做出响应。
在观察者模式中,主题实现了Subject 接口,Subject接口主要定义了三个方法,即注册观察者,通知观察者当前状态以及删除某个观察者。
定义如下:
interface Subject
{
public void registerOberserver(Oberserver o);
public void notifyOberservers(Oberserver o);
public void deleteOberserver(Oberserver o);
}
在各个方法的参数中,Oberserver是观察者接口,具体的观察者实现该接口,这样,在这里,我们体现了面向接口编程的思想。
观察者对象需要实现Oberserber接口,这个接口主要定义了更新的方法,update();这个方法主要是为了主题在更新状态是调用,这样在面向接口编程中,主题只关心接口实现的方法,具体由哪个类实现的,主题并不关心。我们来看相关的类图.
由类图可以看倒,SatelliteData类实现了Subject接口,而DisplayBoard类和Calculate类也分别实现了Oberserver接口,并且SatelliteData拥有多个注册的观察者,观察者共用同一个主题。代码如下:
//主题接口定义
interface Subject
{
public void registerOberserver(Oberserver o);
public void notifyOberservers(Oberserver o);
public void deleteOberserver(Oberserver o);
}
//观察者接口定义
interface Oberserver
{
public void update(float longtitude,float latitude,float lastTime);
}
//类卫星数据定义
class SatelliteData
{
float longitude;
float latitude;
float timeLast;
ArrayList oberservers;//注册的观察者列表
public SatelliteDate()
{
oberservers=new ArralyList();
}
public float getLongitude()
{
……
}
public float getLatitude()
{
……
}
public float getTimeLast()
{
……
}
public setChange()
{
longitude=getLongitude();
latitude=getLatitude();
timeLast=getTimeLast();
notifyOberservers();
}
public void registerOberserver(Oberserver o)
{
oberservers.add(o);
}
public void deleteOberserver(Oberserver o)
{
int i=Oberservers.indexOf(o);
oberservers.remove(i);
}
public void notifyOberservers()
{
longtitude=getLongtitude();
latitude=getLatitude();
timeLast=getTimeLast();
for(int i=0;i<oberservers.size();i++)
{
Oberserver oberserver=(Oberserver)oberservers.get(i);
oberserber.update(longitude,latitude,timeLast);
}
}
}
//显示部门类
class DisplayBoard implements Oberserver
{
SatelliteDate data;
public DisplayData(SatelliteDate data)
{
this.data=data;
}
public void update(float longtitude,float latitude,float timeLast)
{
Syste.out.println("经度"+longtitude +"纬度:"+latitude+"持续时长"+timeLast);
}
//计算类
class Calculate implements Oberserver
{
SatelliteDate data;
public DisplayData(SatelliteDate data)
{
this.data=data;
}
public void update(float longtitude,float latitude,float timeLast)
{
//这是计算过程
}
}
观察者模式总结:
1,在面向对象设计过程中,依赖关系必不可少,我们经常在为对象间的松耦合设计而努力,这是我们进行OO设计的又一个准则,除了面向接口编程以外,还需要的是把是一个变为有一个(is a to has a)。
2,观察者模式的适用范围,就如定义所言,适用于一对多的依赖关系,一个对象状态的改变影响所有其它对象,比如在银行系统中,客户帐户的利息的计算是依赖于银行的利率的,那银行的利率的改变,必须由银行象所有的客户通知,从而利息的计算结果也会发生变化。这里,银行相当于一个主题,银行客户就是观察者来订阅这个主题。可以在这里应用观察者模式。