一、观察者模式
1、观察者模式的定义
观察者(Observer Pattern)模式
,又叫作发布-订阅(Publish/Subscribe)模式、模型-视图模式。
定义一种一对多的依赖关系,一个主题对象可被多个观察者对象同时监听,使得每当主题对象变化时,所有依赖它的对象都会得到通知并被自动更新。它属于对象行为型模式。
观察者模式的核心是将观察者和被观察者解耦。
关键代码:
在抽象类里有一个 ArrayList 存放观察者们。
2、观察者模式的结构
实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。
(1)模式的结构
观察者模式的主要角色如下:
- 抽象主题(Subject):
也叫抽象目标类,是指被观察者对象,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。 - 具体主题(Concrete Subject):
也叫具体目标类/具体被观察者,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。 - 抽象观察者(Observer):
它是一个抽象类或接口,它定义了响应通知的更新方法,当接到具体主题的更改通知时被调用。 - 具体观察者(Concrete Observer):
实现抽象观察者中定义的抽象方法,当得到状态更新的通知时,会自动做出响应。
(2)结构图如下:(图来自网络)
3、优缺点
主要优点:
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
- 目标与观察者之间建立了一套触发机制。
主要缺点:
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
4、使用场景
在软件系统中,当系统一方行为依赖另一方行为的变动时,可使用观察者模式松耦合联动双方,使得一方的变动可以通知到感兴趣的另一方对象,从而让另一方对象对此做出响应。
观察者模式适合场景如下“。
- 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播。
- 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。
5、在框架源码中使用也很广泛
- Spring源码中 ContextLoaderListener实现了 ServletContextLister接口,ServletContextLister又继承了 JDK的 EventListener。
二、模式的通用实现
代码如下:
public class ObserverPattern {
public static void main(String[] args) {
//创建一个主题,添加观察者
Subject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver1();
Observer obs2 = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
//通知
subject.notifyObserver();
}
}
//抽象目标
abstract class Subject {
protected List<Observer> observers = new ArrayList<Observer>();
//增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}
//删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
//具体目标
class ConcreteSubject extends Subject {
@Override
public void notifyObserver() {
System.out.println("具体目标发生改变...");
System.out.println("--------------");
for (Object obs : observers) {
((Observer) obs).response();
}
}
}
//抽象观察者
interface Observer {
void response(); //反应
}
//具体观察者1
class ConcreteObserver1 implements Observer {
public void response() {
System.out.println("具体观察者1作出反应!");
}
}
//具体观察者1
class ConcreteObserver2 implements Observer {
public void response() {
System.out.println("具体观察者2作出反应!");
}
}
三、模式的应用实例
这里用微信公众号为例。
假设微信用户就是观察者,微信公众号是被观察者(可以有不同的主题公众号),多个微信用户关可以关注不同公众号。当公众号更新时就会通知这些订阅的微信用户。
1、抽象主题 - 被观察者
public interface ISubject {
// 增加订阅者
void add(IObserver observer);
// 删除订阅者
void remove(IObserver observer);
//通知订阅者更新消息
void notifyObserver(String message);
}
2、具体主题
具体主题1
public class SubjectOne implements ISubject{
// 储存订阅公众号的微信用户
List<IObserver> observers = new ArrayList<IObserver>();
@Override
public void add(IObserver observer) {
observers.add(observer);
}
@Override
public void remove(IObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObserver(String message) {
observers.forEach(observer -> {
observer.update(message);
});
}
}
具体主题2
public class SubjectTwo implements ISubject {
// 储存订阅公众号的微信用户
List<IObserver> observers = new ArrayList<IObserver>();
@Override
public void add(IObserver observer) {
observers.add(observer);
}
@Override
public void remove(IObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObserver(String message) {
observers.forEach(observer -> {
observer.update(message);
});
}
}
3、抽象观察者 - 观察者
public interface IObserver {
void update(String message);
}
4、具体观察者
public class WechatUser implements IObserver{
// 微信用户名
private String name;
public WechatUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + "," + message);
}
}
5、测试
public static void main(String[] args) {
// 创建两个公众号
ISubject java = new SubjectOne();
ISubject linux = new SubjectTwo();
// 创建四个用户,并订阅
IObserver zhangsan = new WechatUser("张三");
IObserver zhaoyun = new WechatUser("赵云");
IObserver houyi = new WechatUser("后裔");
IObserver luban = new WechatUser("鲁班");
java.add(zhangsan);
java.add(zhaoyun);
java.add(houyi);
java.add(luban);
java.remove(zhangsan);
java.remove(luban);
linux.add(zhangsan);
linux.add(luban);
linux.add(zhaoyun);
// 公众号有更新,通知所有订阅者
java.notifyObserver("您好,java专栏有文章更新了,请注意查看!");
linux.notifyObserver("您好,linux专栏有文章更新了,请注意查看!");
}
四、模式的扩展
在 Java 中,通过 java.util.Observable 类
和 java.util.Observer 接口
定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。
1、java.util.Observable/Observer源码
(1)Observable类
Observable 类是抽象目标类,它有一个 Vector 向量,用于保存所有要通知的观察者对象。
下面来介绍它最重要的 3 个方法。
- void addObserver(Observer o) 方法:用于将新的观察者对象添加到向量中。
- void notifyObservers(Object arg) 方法:调用向量中的所有观察者对象的 update() 方法,通知它们数据发生改变。通常越晚加入向量的观察者越先得到通知。
- void setChange() 方法:用来设置一个 boolean 类型的内部标志位,注明目标对象发生了变化。当它为真时,notifyObservers() 才会通知观察者。
(2)Observer 接口
Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 void update(Observable o,Object arg) 方法,进行相应的工作。
上面认识了观察者模式,我们看源码就看简单了。这里截图几段源码:
2、把上面代码通过 java.util.Observable 来改造
(1)具体主题
public class JavaSubject extends Observable {
//发布通知
public void publishMessage(String message){
//设置内部标志位,注明数据发生变化
super.setChanged();
super.notifyObservers(message);
}
}
(2)具体观察者
public class WechatUser implements Observer {
// 微信用户名
private String name;
public WechatUser(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object message) {
System.out.println(name + "," + message);
}
}
(3)测试
public static void main(String[] args) {
Observer zhaoyun = new WechatUser("赵云");
Observer houyi = new WechatUser("后裔");
Observer luban = new WechatUser("鲁班");
Observable javaSubject = new JavaSubject();
javaSubject.addObserver(zhaoyun);
javaSubject.addObserver(houyi);
javaSubject.addObserver(luban);
javaSubject.deleteObserver(houyi);
Observable linuxSubject = new LinuxSubject();
linuxSubject.addObserver(houyi);
((JavaSubject) javaSubject).publishMessage("您好,java专栏有文章更新了");
((LinuxSubject) linuxSubject).publishMessage("您好,linux专栏有文章更新了");
}
参考文章:
- 设计模式:http://c.biancheng.net/view/1390.html
—— Stay Hungry. Stay Foolish. 求知若饥,虚心若愚。