观察者模式(Observer Pattern),又称为发布/订阅模式。
观察者模式定义了对象之间的一对多的关系,当一个对象发生了变化,其他对象能够得到通知,并做相应处理。
举例:比如我们现在的微信朋友圈,我发布一个朋友圈,那么我的好友都能收到这个新的朋友圈信息,这就就是一个发布/订阅的应用场景。我们试想一下,这个场景应该怎么实现呢?
有两种方式,一种是我的账户每隔一定时间(比如5秒)取一下我当前所有好友的朋友圈状态,看看有没有人发布更新,如果有就拉取。这种方式的一个问题就是,如果用户量巨大,那么每个用户相当于都有一个定时任务,12亿人就是12亿个定时任务。这不是几十台上百台服务器能搞定的。
有的同学会说,可以合并一些用户的定时任务,比如1万个用户作为一个定时任务,在某个时间点启动,检查是否有新的朋友圈状态,这种确实可以,但这也仅仅是减少了定时任务的数量,还是有个问题没有解决,那就是这种定时任务内部还有大量资源浪费,因为这1万个用户里面,还是可能有很多用户压根没有发朋友圈,所以这种方案,在这种场景下并不适用。
那么方案二就是发布订阅了,我所有的好友相当于订阅了我的朋友圈,当我发了一个朋友圈之后,我发布朋友圈这个动作就相当于出发了一个通知事件,这是程序将我的所有好友取出,依次通知每个好友,这样是基于一个事件驱动的,有事件才会触发通知,整体上实现就简单了很多。
我们来看代码:
1、新建抽象观察者
package com.itzhimei.study.design.observer;
/**
* www.itzhimei.com
* 观察者抽象
*/
public interface IObserver {
/**
* 定义通知观察者的方法
* @param message
*/
void update(String message);
}
2、定义具体观察者
package com.itzhimei.study.design.observer;
/**
* www.itzhimei.com
* 订阅者实现
*/
public class ObserverA implements IObserver {
@Override
public void update(String message) {
System.out.println("ObserverA阅读文章内容:"+message);
}
}
package com.itzhimei.study.design.observer;
/**
* www.itzhimei.com
* 订阅者实现
*/
public class ObserverB implements IObserver {
@Override
public void update(String message) {
System.out.println("ObserverB阅读文章内容:"+message);
}
}
package com.itzhimei.study.design.observer;
/**
* www.itzhimei.com
* 订阅者实现
*/
public class ObserverC implements IObserver {
@Override
public void update(String message) {
System.out.println("ObserverC阅读文章内容:"+message);
}
}
3、定义抽象发布者
package com.itzhimei.study.design.observer;
/**
* www.itzhimei.com
* 发布者抽象
*/
public interface ISubject {
/**
* 添加订阅对象
* @param observer
*/
void add(IObserver observer);
/**
* 发布朋友圈
* @param article
*/
void publishArticles(String article);
/**
* 通知方法
* @param article
*/
void notifyAllObserver(String article);
}
4、发布者实现
package com.itzhimei.study.design.observer;
import java.util.ArrayList;
import java.util.List;
/**
* www.itzhimei.com
* 发布者实现
*/
public class Subject implements ISubject {
List<IObserver> observerList = new ArrayList<>();
@Override
public synchronized void add(IObserver observer) {
observerList.add(observer);
}
@Override
public void publishArticles(String article) {
this.notifyAllObserver(article);
}
@Override
public void notifyAllObserver(String article) {
for(IObserver ob: observerList) {
ob.update(article);
}
}
}
5、测试
package com.itzhimei.study.design.observer;
/**
* www.itzhimei.com
* 观察者测试
*/
public class Client {
public static void main(String[] args) {
IObserver oa = new ObserverA();
IObserver ob = new ObserverB();
IObserver oc = new ObserverC();
ISubject subject = new Subject();
subject.add(oa);
subject.add(ob);
subject.add(oc);
subject.publishArticles("今天心情美美哒!");
}
}
输出结果:
ObserverA阅读文章内容:今天心情美美哒!
ObserverB阅读文章内容:今天心情美美哒!
ObserverC阅读文章内容:今天心情美美哒!
发布/订阅模式UML关系图:
发布/订阅涉及角色:
1、抽象主题角色:定义发布者行为
2、具体主题角色:实现消息、状态通知、持有订阅者集合
3、抽象订阅角色:定义订阅者行为
4、具体订阅角色:实现订阅者行为
总结:
发布/订阅模式,从上例可以看出,就是数据是推数据还是拉数据,我们举的反面例子是拉数据,用定时任务不停的扫描,拉去最新数据;我们用的发布/订阅模式,则是通过事件驱动的推数据,简化了整体代码逻辑,让系统更加易于维护。