观察者模式:
定义对象之间的一对多(包含一对一)的依赖关系,当一方对象的状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新
在Subject(目标对象)中,维护了一个观察者的列表,通过attach(Observer o) 添加观察者,detach(Observer o)删除观察者,并通过Notify()方法通知所有的观察者进行更新
这里给一个具体的案例,方便理解。我的朋友小明在天气局工作,为了讨好她的女朋友和老妈,它决定给他们推送最及时最新的天气情况。这里可以用到观察者模式
拉模型的通用代码:将目标对象本身作为参数传递到观察者中,当观察者需要某些信息时主动到目标对象中去拉数据
public class Subject
{
private List<Observer> observers = new ArrayList<Observer>();
public void attach(Observer observer)
{
observers.add(observer);
}
public void detach(Observer observer)
{
observers.remove(observer);
}
public void notifyObserver() //没有参数
{
for (Observer observer : observers)
{
observer.update(this); //调用观察者的update方法,并将当前对象作为参数传递过去(拉模式)
}
}
}
/**
* 具体的目标对象,负责把有关状态存到相应的观察者对象中
* @author Administrator
*
*/
public class ConcreteSubject extends Subject
{
//目标对象的状态
private String subjectState;
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
this.notifyObserver(); //当更新目标对象的状态信息时, 进行广播通知
}
}
/**
* 这是一个观察者的接口,定义一个更新接口给那些在目标状态发生改变时被通知的对象
* @author Administrator
*
*/
public interface Observer
{
//观察者对象的更新方法,传入目标对象,方便获取相应的目标对象的状态信息
public void update(Subject subject);
}
/*
*具体的观察者实现类,实现观察者自身的更新
*/
public class ConcreteObserver implements Observer
{
//观察者状态,与目标对象的状态保持一致
private String observerState;
private String observerName;
/**
* 获取目标类的状态同步到观察者的状态中
*/
@Override
public void update(Subject subject) //按需取内容
{
observerState = ((ConcreteSubject)subject).getSubjectState();
//...根据获取到的目标对象的状态信息,进行相应的更新操作
System.out.println("收到天气通知的人为:"+ observerName+" 此时的天气信息是:"+observerState);
}
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
}
/*
* 测试方法,典型的推模式
*/
public class test
{
public static void main(String[] args)
{
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver();
observer1.setObserverName("小明的女朋友");
ConcreteObserver observer2 = new ConcreteObserver();
observer2.setObserverName("小明的老妈");
subject.attach(observer1);
subject.attach(observer2);
subject.setSubjectState("天气晴");
}
}
推模型:目标对象只想观察者对象中推送部分有用的信息,观察者无法获得为推送的信息
public void update(String message);
@Override
public void update(String message) //按需取内容
{
<span style="white-space:pre"> </span>observerState = message;
<span style="white-space:pre"> </span>//observerState = ((ConcreteSubject)subject).getSubjectState();
//...根据获取到的目标对象的状态信息,进行相应的更新操作
<span style="white-space:pre"> </span>System.out.println("收到天气通知的人为:"+ observerName+" 此时的天气信息是:"+observerState);
}
public void notifyObserver(String message) //没有参数
{
<span style="white-space:pre"> </span>for (Observer observer : observers)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>observer.update(message); //调用观察者的update方法,并将当前对象作为参数传递过去(拉模式)
<span style="white-space:pre"> </span>}
}
public void setSubjectState(String subjectState) {
<span style="white-space:pre"> </span>this.subjectState = subjectState;
<span style="white-space:pre"> </span>this.notifyObserver(subjectState); //当更新目标对象的状态信息时, 进行广播通知
}
利用java提供的观察者实现
//目标类,实现java的Observable
public class ConcreteSubject extends Observable
{
//要推送的信息
private String message;
public void setMessage(String message) {
this.message = message;
//通知之前的必要步骤
this.setChanged();
this.notifyObservers(); //拉模型,会以目标对象自身作为参数,值传递目标对象
//this.notifyObservers(message); //推模型,只推送部分需要的信息,会同时把该目标对象传递过去
}
public String getMessage() {
return message;
}
}
public class ConcreteObserver implements Observer {
private String name;
@Override
public void update(Observable o, Object arg) {
System.out.println("收到信息的观察者是:"+name+" 收到的信息是"+arg);
System.out.println("收到信息的观察者是:"+name+" 收到的信息主动到目标对象中取拉:"+ ((ConcreteSubject)o).getMessage());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/*
* 测试方法,同时实现推拉
*/
public class test
{
public static void main(String[] args)
{
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver();
observer1.setName("小明的女朋友");
ConcreteObserver observer2 = new ConcreteObserver();
observer2.setName("小明的老妈");
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.setMessage("今天下雨");
}
}
观察者模式优缺点 (触发联动)
1>实现了观察者与目标之间的抽象耦合
2>实现了动态联动
3>支持广播通信
4>可能会引起无谓的操作
何时使用观察者模式:
1>当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化
2>如果在更改一个对象时,需要同时连带改变其他的对象,而且不知道究竟有多少需要被连带改变
3>当一个对象必须通知其他的对象,但是又希望这个对象和其他的对象之间松散耦合
如果不同的目标对象的更新信息需要通知不同的和观察者,则在Subject中定义抽象notify()方法,具体实现留到其子类中,根据不同的观察者以及不同的信息进行通知.。