观察者模式
例子
韩非子和李斯。李斯监控韩非子,报信给秦王。
public interface IHanFeiZi {
void haveBreakfast();
void haveFun();
}
public interface Observable {
void addObserver(Observer observer);
void deleteObserver(Observer observer);
void notifyObservers(String context);
}
public interface Observer {
void update(String context);
}
public class HanFeiZi implements Observable,IHanFeiZi {
private ArrayList<Observer> observerArrayList = new ArrayList<>();
@Override
public void addObserver(Observer observer) {
this.observerArrayList.add(observer);
}
@Override
public void deleteObserver(Observer observer) {
this.observerArrayList.remove(observer);
}
@Override
public void notifyObservers(String context) {
for (Observer observer : observerArrayList) {
observer.update(context);
}
}
@Override
public void haveBreakfast() {
System.out.println("韩非子:开始吃饭");
this.notifyObservers("韩非子在吃饭");
}
@Override
public void haveFun() {
System.out.println("韩非子:开始娱乐");
this.notifyObservers("韩非子在娱乐");
}
}
public class LiSi implements Observer {
@Override
public void update(String context) {
System.out.println("李斯:观察到韩非子活动,开始向老板汇报了...");
this.reportToQinShiHuang(context);
System.out.println("李斯:汇报完毕...\n");
}
private void reportToQinShiHuang(String reportContext) {
System.out.println("李斯:报告,秦老板!韩非子有活动了-->" + reportContext);
}
}
public class LiuSi implements Observer {
@Override
public void update(String context) {
System.out.println("刘斯:观察到韩非子活动,开始动作了...");
this.happy(context);
System.out.println("刘斯:乐死了\n");
}
private void happy(String context) {
System.out.println("刘斯:因为" + context + ",--所以我快乐呀!");
}
}
public class WangSi implements Observer {
@Override
public void update(String context) {
System.out.println("王斯:观察到韩非子活动,自己也开始活动了...");
this.cry(context);
System.out.println("王斯:哭死了...\n");
}
private void cry(String context) {
System.out.println("王斯:因为" + context + ",--所以我悲伤呀!");
}
}
public class Client {
public static void main(String[] args) {
Observer liSi = new LiSi();
Observer wangSi = new WangSi();
Observer liuSi = new LiuSi();
HanFeiZi hanFeiZi = new HanFeiZi();
hanFeiZi.addObserver(liSi);
hanFeiZi.addObserver(wangSi);
hanFeiZi.addObserver(liuSi);
hanFeiZi.haveBreakfast();
}
}
定义
观察者模式(Observer Pattern)也叫做发布订阅模式(Publish/subscribe)。定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
- Subject被观察者
定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类 或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。 - Observer观察者
观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。 - ConcreteSubject具体的被观察者
定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。 - ConcreteObserver具体的观察者
每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。
public abstract class Subject {
private Vector<Observer> observerVector = new Vector<>();
public void addObserver(Observer o){
this.observerVector.add(o);
}
public void delObserver(Observer o){
this.observerVector.remove(o);
}
public void notifyObservers(){
for (Observer o : this.observerVector) {
o.update();
}
}
}
public class ConcreteSubject extends Subject{
public void doSomething(){
super.notifyObservers();
}
}
public interface Observer {
void update();
}
public class ConcreteObserver implements Observer{
@Override
public void update() {
System.out.println("接收到信息,并进行处理!");
}
}
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();
subject.addObserver(observer);
subject.doSomething();
}
}
优点
- 观察者和被观察者之间是抽象耦合
不管是增加观察者还是被观察者都非常容易扩展,而且在Java中都已经实现的抽象层级的定义,在系统扩展方面更是得心应手。 - 建立一套触发机制
观察者模式可以完美地实现链条形式。
缺点
观察者模式需要考虑一下开发效率和运行效率问题,一个被观察者,多个观察者,开发和调试就会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。 多级触发时的效率更是让人担忧。
使用场景
- 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
- 事件多级触发场景。
- 跨系统的消息交换场景,如消息队列的处理机制。
注意
- 广播链的问题
在一个观察者模式中 最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次),这还是比较好控制的。 - 异步处理问题
扩展
java本身就有 java.util.Observable
和java.util.Observer
类
public class HanFeiZi extends Observable implements IHanFeiZi {
@Override
public void haveBreakfast() {
System.out.println("韩非子:开始吃饭了...");
//通知所有的观察者
super.setChanged();
super.notifyObservers("韩非子在吃饭");
}
@Override
public void haveFun() {
System.out.println("韩非子:开始娱乐了...");
super.setChanged();
this.notifyObservers("韩非子在娱乐");
}
}
public class LiSi implements Observer {
@Override
public void update(Observable o, Object object) {
System.out.println("李斯:观察到韩非子活动,开始向老板汇报了...");
this.reportToQinShiHuang(object.toString());
System.out.println("李斯:汇报完毕...\n");
}
private void reportToQinShiHuang(String reportContext) {
System.out.println("李斯:报告,秦老板!韩非子有活动了--->" + reportContext);
}
}
大家看看我们使用了Java提供的观察者模式后 是不是简单了很多,所以在Java的世界里横行时,多看看API,有帮助很大,很多东西Java已经帮你设计了一个良好的框架。
项目中真实的观察者模式
- 观察者和被观察者之间的消息沟通
被观察者状态改变会触发观察者的一个行为,同时会传递一个消息给观察者,这是正确的,在实际中一般的做法是:观察者中的update方法接受两个参数,一个是被观察者,一个 是DTO(Data Transfer Object,据传输对象),DTO一般是一个纯洁的JavaBean,由被观察者 生成,由观察者消费。 当然,如果考虑到远程传输,一般消息是以XML格式传递。 - 观察者响应方式
一是采用多线程技术,甭管是被观察者启动线程还是观察者启动线程,都可以明显地提高系统性能,这也就是大家通常所说的异步架构;二是缓存技术,准备了足够的资源,保证快速响应,这当然也是一种比较好方案,代价就是开发难度很大,而且压力测试要做的足够充 分,这种方案也就是大家说的同步架构。 - 被观察者尽量自己做主
对被观察者的业务逻辑 doSomething方法实现重载,如增加一个doSomething(boolean isNotifyObs)方法,决定是否通知观察者,而不是在消息到达观察者时才判断是否要消费。
最佳实践
- 文件系统
比如,在一个目录下新建立一个文件,这个动作会同时通知目录管理器增加该目录,并通知磁盘管理器减少1KB的空间,也就说“文件”是一个被观察者,“目录管理器”和“磁盘管理器”则是观察者。 - 广播收音机
电台在广播,你可以打开一个收音机,或者两个收音机来收听,电台就是被观察者,收音机就是观察者。