目录
一、观察者模式特点
- 定义一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己;
- 观察者与被观察者之间是解耦合的,观察者存在与否不会对被观察者造成影响;
- 观察者模式也叫发布-订阅模式,或模型-视图模式等,被广泛应用于分布式框架或者组件中,如Zookeeper中的统一配置管理;
二、结构分析
观察者模式下有四种角色:
- 抽象主题角色(Subject):也叫抽象被观察者角色(Observable),会将所有观察者(Observer)对象的引用保存在一起,每个抽象主题都可以对应多个观察者。抽象主题提供一个接口,可以增减观察者对象。
- 具体主题角色(ConcreteSubject):实际上是发布者,实现抽象主题提供的接口,实现具体的业务功能,如增删观察者,向登记的观察者通知业务的动态变化。
- 抽象观察者角色(Observer):也叫更新角色,当得知主题发生变化时,为所有的具体观察者提供一个更新自身状态的接口。
- 具体观察者角色(ConcreteObserver):实际上是订阅者,实现抽象观察者提供的接口,保持自身变化与主题变化相协调。
三、场景举例
Zookeeper统一配置管理的例子中:
config配置目录节点就是主题角色(发布者),Client1 ~ ClientN 就是观察者角色(订阅者)。
Demo实现:
- Zookeeper的抽象主题接口:(用于提供发布者具有的功能)
public interface ZKSubject {
void registerClient(ZKClient zkClient); // 注册监听的客户端
void deleteClient(ZKClient zkClient); // 删除监听的客户端
void informAllClient(String newStatus); // 通知所有的客户端
}
- Zookeeper的具体主题实现:(可增删观察者、向观察者们发出主题变化通知)
public class ZKSubjectConfig implements ZKSubject {
// 注册并监听的客户端列表
private List<ZKClient> zkClientList = new ArrayList<>();
@Override
public void registerClient(ZKClient zkClient) {
if (!zkClientList.contains(zkClient)) {
System.out.println("注册了一个客户端.....");
zkClientList.add(zkClient);
}
}
@Override
public void deleteClient(ZKClient zkClient) {
if (zkClientList.contains(zkClient))
zkClientList.remove(zkClient);
}
@Override
public void informAllClient(String newStatus) {
String status = newStatus;
for (ZKClient zkClient : zkClientList) {
zkClient.update(status);
}
}
}
- Zookeeper的抽象观察者接口:(用于更新)
public interface ZKClient {
void update(String status);
}
- Zookeeper的具体观察者实现:(可以有多个观察者,保持自身与主题变化相一致)
public class ZKClientListener1 implements ZKClient {
@Override
public void update(String status) {
System.out.println("1号客户端收到zk的config节点变化的通知,变化状态为:" + status);
}
}
public class ZKClientListener2 implements ZKClient{
@Override
public void update(String status) {
System.out.println("2号客户端收到zk的config节点变化的通知,变化状态为:" + status);
}
}
测试:
@Test
void testObserver() {
// 创建主题和观察者(即发布者和订阅者,一对多)
ZKSubjectConfig subject = new ZKSubjectConfig();
ZKClientListener1 clientListener1 = new ZKClientListener1();
ZKClientListener2 clientListener2 = new ZKClientListener2();
// 注册客户端,客户端会放入监听主题的客户端列表中
subject.registerClient(clientListener1);
subject.registerClient(clientListener2);
// 发布者写入配置文件配置,向所有监听它的客户端通知这件事情
subject.informAllClient("写入配置消息");
}
结果:
四、Java中对观察者模式的支持
在java.util包下提供了Observer类作为观察者,Observable类作为被观察者,看一下源码:
// 观察者
public interface Observer {
void update(Observable o, Object arg);
}
// 被观察者
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
public Observable() {obs = new Vector<>();}
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
protected synchronized void setChanged() {changed = true;}
protected synchronized void clearChanged() {changed = false;}
public synchronized boolean hasChanged() {return changed;}
}
接着上面的例子看下如何使用:
// 被观察者状态改变的实现类
public class SubjectStatus extends Observable {
private String status = "";
public String getStatus() {
return status;
}
public void changeStatus(String newStatus){
if (!newStatus.equals(this.status)){
this.status = newStatus;
setChanged();
notifyObservers();
}
}
}
// 第1个观察者
public class Client1 implements Observer {
// 通过构造器把自己加入监听被观察着的列表中
public Client1(Observable o) {
System.out.println("Client1已加入监听列表....");
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
System.out.println("Client1收到节点变化通知,状态为:" + ((SubjectStatus) o).getStatus());
}
}
// 第2个观察者
public class Client2 implements Observer {
// 通过构造器把自己加入监听被观察着的列表中
public Client2(Observable o) {
System.out.println("Client2已加入监听列表....");
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
System.out.println("Client2收到节点变化通知,状态为:" + ((SubjectStatus) o).getStatus());
}
}
测试一下:
@Test
void testObserver() {
// 被观察者
SubjectStatus sc = new SubjectStatus();
// 多个观察者
Client1 c1 = new Client1(sc);
Client2 c2 = new Client2(sc);
logger.info("\n当前监听者的个数:" + sc.countObservers());
sc.changeStatus("新增配置");
}
输出结果:
【不积硅步无以至千里,共同进步吧~】