观察者模式允许多个观察者订阅一个主题,当主题的状态发生变化时,能够将这种变化通知到每个观察者。从主题的角度看,这是典型的一对多关系,即一个主题可以对应多个观察者。以订阅邮件为例,当订阅某一类主题内容(例如娱乐新闻,动漫等)时,如果该主题的内容有更新,那么每一个订阅该主题的人都会受到一封内容更新的邮件,这便是典型的观察者模式。
要实现邮件订阅的功能,需要先定义一个主题接口,主题接口需要能够随时添加或者删除订阅人(观察者),并且在有内容更新时通知观察者,其定义如下:
1 public interface Subject { 2 //添加观察者 3 void registerObserver(Observer observer); 4 5 //移除观察者 6 void removeObserver(Observer observer); 7 8 //有新内容时通知观察者 9 void notifyObserver(); 10 }
与主题接口对应的是观察者接口,接口很简单,在有内容的时候更新状态:
1 public interface Observer{ 2 void update(); //更新状态 3 }
主题接口有许多实现,这里选择娱乐新闻主题作为例子:
1 import java.util.ArrayList; 2 import java.util.List; 3 4 public class EntertainmentSubject implements Subject{ 5 6 List<Observer> observerList; 7 8 public EntertainmentSubject(){ 9 this.observerList = new ArrayList<>(); 10 } 11 12 @Override 13 public void registerObserver(Observer observer) { 14 this.observerList.add(observer); 15 } 16 17 @Override 18 public void removeObserver(Observer observer) { 19 int i = observerList.indexOf(observer); 20 if(i >= 0){ 21 observerList.remove(i); 22 } 23 } 24 25 @Override 26 public void notifyObserver() { 27 for(Observer observer : observerList){ 28 observer.update(); 29 } 30 } 31 32 // 假设内容变化时会调用这个接口 33 public void contentUpdated(){ 34 notifyObserver(); 35 } 36 }
主题需要在内容发生变化的时候通知所有的观察者,这里使用contentUpdated()方法来实现,至于主题内容变化如何调用contentUpdated()方法则不在本文的讨论范围内,这里假设只要主题有更新就会调用这个方法。
接下来定义两个具体的观察者:
1 public class ConcreteObserver1 implements Observer{ 2 3 Subject subject; 4 public ConcreteObserver1(Subject subject) { 5 this.subject = subject; 6 } 7 8 @Override 9 public void update() { 10 System.out.println("ConcreteObserver1, 给你发个邮件,因为这里有新内容了。。。"); 11 } 12 }
1 public class ConcreteObserver2 implements Observer{ 2 3 Subject subject; 4 public ConcreteObserver2(Subject subject) { 5 this.subject = subject; 6 } 7 8 @Override 9 public void update() { 10 System.out.println("ConcreteObserver2, 给你发个邮件,因为这里有新内容了。。。"); 11 } 12 }
好了,所有的类都准备就绪,是时候写个测试来看一下观察者模式的使用了:
1 public class ObserverTest { 2 public static void main(String[] args){ 3 EntertainmentSubject subject = new EntertainmentSubject(); 4 ConcreteObserver1 observer1 = new ConcreteObserver1(subject); 5 ConcreteObserver2 observer2 = new ConcreteObserver2(subject); 6 subject.registerObserver(observer1); 7 subject.registerObserver(observer2); 8 subject.contentUpdated(); //模拟内容更新 9 10 subject.removeObserver(observer1); //observer退订 11 subject.contentUpdated(); 12 } 13 }
输出:
ConcreteObserver1, 给你发个邮件,因为这里有新内容了。。。
ConcreteObserver2, 给你发个邮件,因为这里有新内容了。。。
ConcreteObserver2, 给你发个邮件,因为这里有新内容了。。。
开始的时候有两个观察者,内容更新的时候两个观察者都收到了通知,后来observer1取消了订阅,更新的时候就不再通知这个观察者了。
当然,这里的观察者模式的逻辑很简单,现实中用到的观察者模式比这要复杂许多,不过底层的结构大体相似。而且,Java也提供了观察者模式的相关实现Observable和Observer,使用的时候需要将Subject换成Observable,需要注意的是Observable是类而不是接口,所以如果你的类已经继承了其他类,就无法使用Java的这套API了。
参考:
<<Head First设计模式>>