设计模式是进阶高级开发的必经之路。掌握设计模式,才能提升编码能力,设计出可复用、可扩展、可维护的软件系统。了解设计模式,才能更好理解开源类库的实现原理,解决问题。
观察者模式(Observer Pattern)是《Head First 设计模式》介绍的第二个模式。本文将做介绍,并介绍此模式在JDK中的应用。
定义
观察者模式定义了对象之间的一(被观察者)对多(观察者)依赖,这样一来,当一个对象(被观察者)改变状态时,它的所有依赖者(观察者)都会收到通知并自动更新。
应用场景举例
- 用户订阅报社报纸。当报社印刷新报纸(状态改变),向所有订阅用户投递报纸(推送消息)。用户可随时取消、加入订阅。
- 火车站的车次显示屏,显示车次到达发车信息。当火车数据更改时,每个显示屏都要及时更新数据。等等
分析
上述两个例子中,可以抽象出一对多的关系,比如报社对应多个用户,火车数据对应多个显示屏。用户、显示屏等是观察者,报社、火车数据是被观察者。而且观察者的数量动态变化的。有人退出,有人加入,类型不固定(有公司订阅报纸,有微信、微博发布火车时刻信息)。
如果用java实现这样的功能。自然的,可以抽象出被观察者类、观察者类。
观察者可能会观察多个数据源,一个数据源也会被不同类型的观察者关注。若在类中指定观察者、被观察者具体类型(比如报社——人,火车数据——显示屏),以后报社新增公司客户(新增观察者),火车数据新增广播平台(新增观察者),一个客户订阅另一本杂志(新增被观察者),我们就得去修改代码,糟透了。
此处,一个新的设计原则:为了交互对象之间松设计而努力诞生了。
我们应该把观察者、被观察者抽象成抽象类或者接口,定义公共的方法。让以后的观察者、被观察者实现这些接口或抽象类即可。考虑到另一个原则:针对接口编程,我们采用接口抽象。
为了能及时追踪被观察者的变化。一种方式是观察者无间断盯着被观察者,不断的问:··你到底变没变?你到底变没变?
//观察者
while(true){
//问被观察者:你到底变没变
if(观察者.hasChanged()){
//do something
}
}
很明显,这种方式,彼此压力都很大,不是个好设计。
另一种方式,就是当被观察者发生改变时,主动的通知观察者(调用观察者某个方法),观察者随机做出反应:
//被观察者通知观察者
notifyObservers(){
//依次通知,也可以设计成单个通知
for(){
观察者.update("喂,我变帅啦");
}
}
//回执
result(观察者 观察者1,String info){
System.out.println(观察者1.class.getName()+"发来消息:"+info)
}
-------------------------------------------
//观察者
update(String info){
System.out.println("收到消息,被观察者:"+info);
//甚至可以设置个回执,告诉被观察者,你的消息我收到了
//传入观察者自己,好让被观察者知道谁给他发的回执
被观察者.result(观察者,"我收到你的消息了");
}
这才是我们想要的。再加上新增、删除观察者功能,我们基本上就实现了观察者模式设计了。
代码
根据面向接口编程原则,把观察者、被观察者抽象成接口:
//被观察者Observered接口
public interface Observered {
//新增一个观察者
void addObserver(Observer observer);
//删除某个观察者
void removeObserver(Observer observer);
//通知所有观察者
void notifyObserver();
}
//-----------------------------------------
//观察者Observer接口
public interface Observer {
//被观察者调用。
//@param observered 被观察者(有可能当前观察者关注了多个数据源)
//@param obj 传入的数据。自定义类型,此处用Object
void update(Observered observered,Object obj );
}
接下来就是具体的实现类。此处我们安排三个观察者,两个被观察者。场景:
Aboy
、Bboy
在淘宝上买了东西,淘宝Taobao
需要推送发货信息给Aboy
和Bboy
,Aboy
和Cgirl
关注了一个公众号WeChatPublicSignal
(姑且这么叫吧),公众号会推送文章给这俩人。
首先两个被观察者:Taobao
和WeChatPublicSignal
class Taobao implements Observered{
//用LinkedList<E>或其他结构
List<Observer> observerList = new ArrayList<Observer>();
@Override
public void addObserver(Observer observer) {
//添加一个观察者
observerList.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observerList.remove(observer); //移除一个观察者
}
@Override
public void notifyObserver() {
//遍历,通知所有观察者并发送消息
for (Observer observer : observerList) {
System.out.println("Taobao说:我正在发数据给"+observer.getSimpleName().getName());
//此处的数据参数可以灵活设计
observer.update(this, "你买的娃娃已经发货啦___");
}
}
}
class Taobao implements Observered{
//其他代码同Taobao,略,只改动发送的数据
observer.update(this, "咪蒙说:外国人那么大,我想去看看");
}
三个观察者Aboy、Bboy和Cgirl
//实现观察者接口
class Aboy implements Observer{
@Override
public void update(Observered observered, Object obj) {
System.out.println("Aboy------说:我接收到--"+observered.getClass().getSimpleName()+"--发来数据: "+obj);
}
}
//Bboy、Cgril同Aboy,略
测试:
public static void main(String[] args) {
//实例化观察者
Observer aboy = new Aboy();
Observer bboy = new Bboy();
Observer cgirl= new Cgirl();
Observered taobao = new Taobao();//被观察者Taobao
//添加观察者
taobao.addObserver(aboy);
taobao.addObserver(bboy);
//通知观察者
taobao.notifyObserver();
//下同
Observered wechat = new WeChatPublicSignal();
wechat.addObserver(aboy);
wechat.addObserver(cgirl);
wechat.notifyObserver();
}
控制台输出:
Taobao----说:我正在发数据给--Aboy
Aboy------说:我接收到--Taobao--发来数据: 你买的娃娃已经发货啦___
Taobao----说:我正在发数据给--Bboy
Bboy------说:我接收到--Taobao--发来数据: 你买的娃娃已经发货啦___
WeChatPublicSignal说:我正在发数据给Aboy
Aboy------说:我接收到--WeChatPublicSignal--发来数据: 咪蒙说:外国人那么大,我想去看看
WeChatPublicSignal说:我正在发数据给Cgirl
Cgirl-----说:我接收到--WeChatPublicSignal--发来数据: 咪蒙说:外国人那么大,我想去看看
结果符合设计。
JDK中的应用
观察者模式在JDK中有实现。java.util.Observer
就是JDK实现的观察者接口,其中就有一个update方法:
//观察者接口
public interface Observer {
void update(Observable o, Object arg);
}
而被观察者java.util.Observable
是采用类被继承的方式设计的:
/*JDK中被观察者类的实现
*其他代码删除了,具体参考JDK源码
* @since JDK1.0
*/
public class Observable {
private boolean changed = false;
private Vector obs;//存储观察者的集合
public Observable() {
obs = new Vector();
}
//添加观察者
public synchronized void addObserver(Observer o) {
}
//删除观察者
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
//通知观察者
public void notifyObservers(Object arg) {
}
}
我们设计的基本功能Observable中都有实现,且新增了不少方法。还考虑了线程安全、效率问题等。美中不足的是,你如果想使用这个类,那就得继承它,这势必会影响你的其他设计(单继承)。自己设计一个被观察者接口也很简单,随便你咯。
总结
- 观察者模式定义了对象间一对多的关系。
- 观察者和被之间用松耦合的方式连接,彼此都不知道对方是谁,只知道发出、接收数据,像网恋一样,不管对方是周杰伦还是凤姐,也能快乐的聊天恋爱。
- 可以更进一步设计数据的获取方式,推或者拉。