前言:基础的观察者模式:大话设计模式——观察者模式(一)
一、再识观察者模式
1.1 目标与观察者
目标与观察者的关系是典型的一对多关系,当然,实现一对一也是可以的。
一个观察者,可以关注多个目标,一个目标也可以被多个观察者观察。
比如,小陈的女朋友和他老妈可以同时关注天气信息。而小陈的女朋友也可以同时关注天气和报纸。需要注意的是,这时的通知方法要进行区分。
1.2 单向依赖
观察者依赖目标。主动权在目标手中,观察者只有等待。
1.3 命名建议
- 目标接口的定义,在名称后跟subject
- 观察者接口的定义,在名称后跟observer
- 观察者接口的更新方法,名称为update
1.4 触发通知的时机
应该先更新对应的值,在去通知观察者。
1.5 调用顺序
- 准备阶段
- 创建目标对象
- 创建观察者对象
- 向目标对象注册观察者对象
- 运行阶段
- 改变目标对象的状态
- 通知所有注册的观察者对象进行相应的处理
- 回调目标对象,获取相应的数据
1.6通知的顺序
通知的顺序应该是平行的,相互不能有依赖的关系
二、推拉模型
2.1 推模型
目标对象主动向观察者推送目标的详细信息
推送的信息通常是目标对象的全部或者部分数据
2.2 拉模型
目标对象在通知观察者的时候,只传递少量信息。
如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。
一般这种模型中,会把目标对象自身通过update传递给观察者(上一节的订阅天气就是一个典型的拉模型)
2.3 代码实现
首先在Observer接口中,新增一个方法,按需求定制相应方法。
/**
* 推模型,按需定制
* @param content 天气内容
*/
void updateWeatherContent(String content);
在ConcreatObserver中,实现此方法
@Override
public void updateWeatherContent(String content) {
weatherContent = content;
System.out.println(observerName+"收到了"+weatherContent+","+remindThing);
}
再来到WeatherSubject中,修改notifyObservers方法
/**
* 通知所有已经订阅了天气的人
* @param content 天气信息
*/
protected void notifyObservers(String content){
for(Observer observer:observers){
observer.updateWeatherContent(content);
}
}
最后,来到ConcreatWeatherSubject类,修改setWeatherContent方法,传入天气信息。
public void setWeatherContent(String weatherContent) {
this.weatherContent = weatherContent;
//天气更新,通知所有订阅天气的观察者
this.notifyObservers(weatherContent);
}
2.4 比较
- 推模型是假定目标知道观察者需要的数据,观察者难以复用
- 拉模型是目标对象不知道观察者要什么样的数据,就将自身传给观察者
三、利用Java提供的观察者实现
3.1 目标类
我们的目标类继承Java提供的Observable
public class ConcreteWeatherSubject extends Observable {
//天气情况
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
//通知所有观察者
//Java中的Observer模式中,这句话不可少
this.setChanged();
//推方法
this.notifyObservers(content);
//拉方法
//this.notifyObservers();
}
}
3.2 观察者
实现Java提供的Observer接口,实现update方法。其中o为传递过来的目标对象(拉方式),arg为推过来的内容(推方式)。
public class ConcreteObserver implements Observer{
//观察者名称变量
private String observerName;
@Override
public void update(Observable o, Object arg) {
//推方式
System.out.println(observerName + "收到了消息,推送过来的是" + arg);
//拉方式
System.out.println(observerName + "收到了消息,主动到目标对象"
+ "中去拉消息,拉的内容是" +
((ConcreteWeatherSubject)o).getContent());
}
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
}
3.3 测试类
public class Client {
public static void main(String[] args) {
//创建天气
ConcreteWeatherSubject subject = new ConcreteWeatherSubject();
//创建观察者
ConcreteObserver girl = new ConcreteObserver();
girl.setObserverName("GF");
ConcreteObserver mom = new ConcreteObserver();
mom.setObserverName("MOM");
//注册
subject.addObserver(girl);
subject.addObserver(mom);
//目标更新天气
subject.setContent("台风14级");
}
}
四、优缺点
4.1 优点
- 实现了观察者和目标之间的抽象耦合
- 实现了动态联动
- 支持广播通信
4.2 缺点
- 可能引起无谓操作(一些观察者可能不需要某些信息,也可能引起误更新)