观察者模式,顾名思义需要两方,一方是被观察的(主题),另一方就是观察者。就拿直播间来说,直播间就相当于一个主题,而用户就是观察者,用户进入直播间就是向主题注册(加进了用户队列),一般来说,用户可以离开直播间(从主题中移除该用户),当然直播间的主播也可以将用户移出直播间(也相当于从主题中移除该用户)。也就是说其实主题和观察者的关系是一对多的关系,主题数据的变化,观察者能及时知道。
下面进行观察者模式的实现,首先是主题接口:
1 package cn.liu.java.myjava.Interface; 2 /** 3 * 这是主题接口,主要作用是注册观察者,移除观察者还有就是通知观察者 4 * 假设这里的应用场景是告知有当前注册的用户,现在有多少个观察者在监听 5 * @author Administrator 6 * 7 */ 8 public interface Subject { 9 //注册观察者10 public void registerObserver(Observer observer);11 //移除观察者12 public void removeObserver(Observer observer);13 //通知所有观察者14 public void notifyObservers();15 }
接下来是观察者接口:
1 package cn.liu.java.myjava.Interface; 2 /** 3 * 观察者接口,该观察者主要是知道当前有多少个观察者注册了(相当于在线场景) 4 * @author Administrator 5 * 6 */ 7 public interface Observer { 8 //观察者被通知时候调用的方法 9 public void show(int num);10 //观察者主动注册11 public void register();12 //观察者主动取消订阅13 public void remove();14 }
接下来是主题接口的具体实现类:
1 package cn.liu.java.myjava; 2 3 import java.util.LinkedList; 4 import java.util.List; 5 6 import cn.liu.java.myjava.Interface.Observer; 7 import cn.liu.java.myjava.Interface.Subject; 8 9 /**10 * 主题的实现类11 * @author Administrator12 *13 */14 public class SubjectImpl implements Subject{15 //主题要通知观察者,那么必须在主题中注册观察者,用一个list来存储观察者。16 //因为linkedlist的实现是使用了链表的实现方式,观察者模式经常需要增删用户,17 //删除用户如果用链表就只需要调整指针,不需要移动后面的元素18 private Listobservers = new LinkedList ();19 20 public void registerObserver(Observer observer) {21 // TODO Auto-generated method stub22 observers.add(observer);23 //添加了观察者就通知所有的观察者对象24 notifyObservers();25 }26 27 public void removeObserver(Observer observer) {28 // TODO Auto-generated method stub29 observers.remove(observer);30 notifyObservers();31 }32 33 public void notifyObservers() {34 // TODO Auto-generated method stub35 Observer observer = null;36 for (int i = 0; i < observers.size(); i++) {37 if ((observer = observers.get(i)) != null) {38 observer.show(observers.size());39 }40 }41 }42 43 44 }
最后就是观察者的具体实现:
package cn.liu.java.myjava;import cn.liu.java.myjava.Interface.Observer;import cn.liu.java.myjava.Interface.Subject;/** * 观察者的实现 * @author Administrator * */public class ObserverImpl implements Observer{ //观察者需要保留一个主题的引用,方便观察者的注册和移除 private Subject subject; //为每个观察者给定一个名字 private String name; public ObserverImpl(Subject subject , String name) { this.subject = subject; this.name = name; } public void show(int num) { // TODO Auto-generated method stub System.out.println(name+"收到通知:现在有"+num+"人注册了该服务"); } public void register() { // TODO Auto-generated method stub subject.registerObserver(this); } public void remove() { // TODO Auto-generated method stub subject.removeObserver(this); } }
接下来就是测试类:
1 package cn.liu.java.myjava; 2 3 import org.junit.Test; 4 5 import cn.liu.java.myjava.Interface.Observer; 6 import cn.liu.java.myjava.Interface.Subject; 7 8 public class MyTest { 9 @Test10 public void testName() throws Exception {11 Subject subject = new SubjectImpl();12 Observer observer1 = new ObserverImpl(subject , "观察者一");13 Observer observer2 = new ObserverImpl(subject , "观察者二");14 Observer observer3 = new ObserverImpl(subject , "观察者三");15 //观察者主动要求注册(其实其中的内部实现也是通过主题对象来注册)16 subject.registerObserver(observer1);17 //通过主题对象来注册18 observer2.register();19 observer3.register();20 observer2.remove();21 subject.removeObserver(observer3);22 observer1.remove();23 observer1.remove();24 }25 }
测试结果如图:
观察者模式基本实现就是这样了,java类库中也有观察者模式的具体接口,可以直接使用java类库来实现观察者模式,上述例子中不太好的就是由主题来进行各个观察者的通知,其实比较好的就是观察者可以主动到主题中抓取数据,这样可以方便不同的观察者按需来抓取需要的数据,减小主题的复杂性。
下面实现观察者主动去提取主题中的数据(size)
1 package cn.liu.java.myjava.Interface; 2 /** 3 * 这是主题接口,主要作用是注册观察者,移除观察者还有就是通知观察者 4 * 假设这里的应用场景是告知有当前注册的用户,现在有多少个观察者在监听 5 * @author Administrator 6 * 7 */ 8 public interface Subject { 9 //注册观察者10 public void registerObserver(Observer observer);11 //移除观察者12 public void removeObserver(Observer observer);13 //通知所有观察者14 public void notifyObservers();15 }
1 package cn.liu.java.myjava.Interface; 2 /** 3 * 观察者接口,该观察者主要是知道当前有多少个观察者注册了(相当于在线场景) 4 * @author Administrator 5 * 6 */ 7 public interface Observer { 8 //观察者被通知时候调用的方法 9 public void show();10 //观察者主动注册11 public void register();12 //观察者主动取消订阅13 public void remove();14 }
1 package cn.liu.java.myjava; 2 3 import java.util.LinkedList; 4 import java.util.List; 5 6 import cn.liu.java.myjava.Interface.Observer; 7 import cn.liu.java.myjava.Interface.Subject; 8 9 /**10 * 主题的实现类11 * @author Administrator12 *13 */14 public class SubjectImpl implements Subject{15 //主题要通知观察者,那么必须在主题中注册观察者,用一个list来存储观察者。16 //因为linkedlist的实现是使用了链表的实现方式,观察者模式经常需要增删用户,17 //删除用户如果用链表就只需要调整指针,不需要移动后面的元素18 private Listobservers = new LinkedList ();19 private int size;20 public void registerObserver(Observer observer) {21 // TODO Auto-generated method stub22 observers.add(observer);23 size = observers.size();24 //添加了观察者就通知所有的观察者对象25 notifyObservers();26 }27 28 public void removeObserver(Observer observer) {29 // TODO Auto-generated method stub30 observers.remove(observer);31 size = observers.size();32 notifyObservers();33 }34 35 public void notifyObservers() {36 // TODO Auto-generated method stub37 Observer observer = null;38 for (int i = 0; i < observers.size(); i++) {39 if ((observer = observers.get(i)) != null) {40 observer.show();41 }42 }43 }44 45 public int getSize() {46 return size;47 }48 49 50 }
1 package cn.liu.java.myjava; 2 3 import cn.liu.java.myjava.Interface.Observer; 4 import cn.liu.java.myjava.Interface.Subject; 5 /** 6 * 观察者的实现 7 * @author Administrator 8 * 9 */10 public class ObserverImpl implements Observer{11 //观察者需要保留一个主题的引用,方便观察者的注册和移除12 private Subject subject;13 //为每个观察者给定一个名字14 private String name;15 public ObserverImpl(Subject subject , String name) {16 this.subject = subject;17 this.name = name;18 }19 public void show() {20 // TODO Auto-generated method stub21 System.out.println(name+"收到通知:现在有"+((SubjectImpl)subject).getSize()+"人注册了该服务");22 }23 public void register() {24 // TODO Auto-generated method stub25 subject.registerObserver(this);26 }27 public void remove() {28 // TODO Auto-generated method stub29 subject.removeObserver(this);30 }31 32 }
测试结果和第一个实现没区别,第二个只是不需要主题传递数据给观察者,观察者直接从主题提取需要的数据。