观察者模式又叫发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。从下方的结构图可以看到,主要分为四个部分:抽象的被观察者、抽象的观察者、具体的被观察者、具体的观察者。
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象之间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。而观察者模式中,一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知,而Subject发出通知时不需要知道谁是它的观察者,而一个观察者也不需要知道其他观察者。当一个对象的改变需要同时改变其他对象,并且它不知道具体有多少对象待改变时,就可以考虑使用观察者模式。或者当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。所以,观察者模式所做的工作就是解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不会影响另一边的变化。
观察者模式的常见使用场景是:消息订阅和推送系统。以此为例来进行具体的说明。现在有大量的用户作为观察者,他们都订阅了某个公众号也就是被观察者,当公众号有状态变化也就是有新的文章发布的时候,就会给每个订阅的用户发送一个通知,公众号不关注具体有哪些用户,只需要保证所有用户都发送了。
因为每个用户有自己不同的信息,所以,需要定义一个抽象的观察者接口,因为状态有更新时被观察者需要通知观察者,所以至少需要一个通知方法。
public interface Observer {
public void update();
}
具体的观察者也就是用户类除了自身的属性外,实现具体的更新方法,还增加了阅读消息方法
public class User implements Observer {
private String name;
private String message;
public User(String name) {
this.name = name;
}
@Override
public void update(String message) {
this.message = message;
}
public void read() {
System.out.println("用户" + name + "阅读消息" + message);
}
}
抽象的被观察者需要定义三个基本方法:增加观察者、移除观察和、通知所有观察者
public interface Subject {
public void attach(Observer ob);
public void detach(Observer ob);
public void notifyObserver();
}
具体的服务器实现如下,需要有一个列表来存储所有的观察者,因为需要产生消息后通知所有的观察者,也就是更新信息后遍历列表调用所有接口的update方法
public class Server implements Subject {
private List<Observer> list = new ArrayList<>();
private String message;
@Override
public void attach(Observer ob) {
list.add(ob);
}
@Override
public void detach(Observer ob) {
list.remove(ob);
}
@Override
public void notifyObserver() {
for(Observer ob : list) {
ob.update(message);
}
}
public void setMessage(String message) {
this.message = message;
System.out.println("服务器产生新的消息" + message);
}
}
测试类如下
public class Test {
public static void main(String[] args) {
Server server = new Server();
User u1 = new User("小王");
User u2 = new User("小李");
server.attach(u1);
server.attach(u2);
server.setMessage("第一条消息");//服务器产生新的消息第一条消息
server.notifyObserver();
u1.read();//用户小王阅读消息第一条消息
u2.read();//用户小李阅读消息第一条消息
server.detach(u2);
server.setMessage("第二条消息");//服务器产生新的消息第二条消息
server.notifyObserver();
u1.read();//用户小王阅读消息第二条消息
u2.read();//用户小李阅读消息第一条消息
}
}
对于事件委托模式,Java中需要利用反射机制来完成。