本文由原创,转载请注明出处:
观察者模式简述
观察者模式的使用非常广泛,常用于建立起一种多对一的关系,该模式一定会包含两个角色,观察者和被观察的对象,当被观察的对象发生变化的时候观察者会接受到相应的通知。
观察者模式的定义与基本结构
定义:定义一个一对多的依赖关系,当被多个对象依赖的那个对象发生改变的时候,所有依赖它的对象都会得到相应的通知并进行状态的更新。
让我们来看一张来自《Head First》的结构图。
Subject:被观察对象的接口,只要实现了这个接口,任何类都可以成为被观察的对象,这种面向接口的编程方式让类之间的耦合度降低。
Observer:观察者对象的接口,只要实现了这个接口,任何类都可以成为被观察的对象,这种面向接口的编程方式让类之间的耦合度降低。
ConcreteSubject:实现了Subject接口的具体实例。
ConcreteObserver:实现了Observer接口的具体实例。
注:一对多指的是一个Subject可以被很多observer观察。
一个简单的实例(java)
从观察者更新信息的方式上,可以将观察者模式的实现分成两类,一类是每次Subject更新了信息,Observer都会收到更新的信息(被称为push方式),另一类是每次Subject更新的时候,只通知Observer信息被更新了,但是需要Observer在需要的时候自己来取新的信息(被称为pull方式)。
我们只给出push方式完整的代码,然后文字叙述pull与push实现的区别,感兴趣的朋友可以自己试着实现pull版本。
让我们来考虑这样一个例子,只要我们定了一份新的杂志,每当这份杂志有新的版本的时候,在push方式下,这本杂志就会被送到每一个订阅者的家里,这里发行商就是ConcreteSubject类,顾客是ConcreteObserver类,实现如下。
Subject接口
1 public interface Subject {2 void addObserver(Observer o);3 void deleteObserver(Observer o);4 void notifyObserver();5 }
Observer接口
1 public interface Observer {2 void update(String magazine);3 }
Publisher类,继承了Subject接口
1 public class Publisher implements Subject { 2 private ArrayListcustomer; 3 private String magazine; 4 public Publisher(){ 5 customer=new ArrayList (); 6 magazine=""; 7 } 8 @Override 9 public void addObserver(Observer o) {10 customer.add(o);11 System.out.println("新顾客加入");12 }13 14 @Override15 public void deleteObserver(Observer o) {16 customer.remove(o);17 System.out.println("老顾客退出");18 }19 @Override20 public void notifyObserver() {21 for(Observer o:customer){22 o.update(magazine);23 }24 }25 public void publishNewMagazine(String magazine){26 this.magazine=magazine;27 notifyObserver();28 }29 }
Customer类,继承了Observer接口
1 public class Customer implements Observer { 2 String name; 3 public Customer(String name){ 4 this.name=name; 5 } 6 @Override 7 public void update(String magazine) { 8 System.out.println(name+" 拿到了新的杂志 "+magazine) ; 9 }10 }
测试程序如下
1 public class Test { 2 public static void main(String[] args) { 3 Publisher publisher=new Publisher(); 4 Customer customer1=new Customer("叶良辰"); 5 Customer customer2=new Customer("龙傲天"); 6 publisher.addObserver(customer1); 7 publisher.addObserver(customer2); 8 publisher.publishNewMagazine("装B指南"); 9 publisher.deleteObserver(customer1);10 publisher.publishNewMagazine("上天秘籍");11 }12 }
输出如下
pull与push主要的区别
1,pull式需要Observer自己去取数据,所以Subject要提供相应的get方法
2,pull式虽然不用主动push数据,但是仍然要在每次更新时告知Observer数据已经更新,所以每一个Observer需要设立一个flag表示自己数据的新旧。
3,object需要判断哪些Observer有权限来取得自己的数据。
(pull方式在那种只有最新信息有价值且Subject更新太快而不适合用push的情况下使用比较好,比如手机上接受天气预报信息之类的)
Java的built-in Observer Pattern
因为观察者模式实在是太常用了,以至于java给我提供了相应的工具直接构建Observer模式,java提供了一个Observable类(作用相当于我们刚刚写的Object,但是这个是类不是接口),和Observer接口,我们只要继承这些类和接口就能直接应用观察者模式了
它们的位置
import java.util.Observable;import java.util.Observer;
Observer类里有以下成员方法
addObserver(Observer o);//添加observerclearChanged();//重置更新标志,hasChanged()的返回值将为falsecountObservers();//返回observer的数量deleteObserver(Observer o);//删除一个特定的observerdeleteObservers();//删除所有observerhasChanged();//测试object的内容距上一次通知后有没有更新notifyObservers();//如果object内容有更新,调用这个方法会通知所有observer,之后会调用clearChange()方法,这个方法是为pull式实现准备的。notifyObservers(Object arg);//如果object内容有更新,调用这个方法会将参数中arg的更新推送给所有observer,之后会调用clearChange()方法,这个方法是为push式实现准备的setChanged();//标明object已经更新
Observer接口里面只有一个update方法,声明如下
public void update(Observable o, Object arg);
第一个参数是被观察的对象,第二个是任何我们想更新给observer的对象,这个对象来自notifyObservers的参数,如果缺失这个参数,表示我们的observer需要自己来拿数据(即pull式实现),如果有这个对象,则属于push式实现。
运用这个java提供的观察者模式的实现我们只需要做两件事。
1,决定使用pull式实现还是push式实现。
2,根据我们的决定实现update方法,如果我们用pull式实现,我们最好在Subject类里提供相应的get方法。
这里给出一个示例,还是上面的例子,仍然是push式,这次我们不用写object与observer接口了。
Customer类
1 public class Customer implements Observer { 2 String name; 3 boolean newManagzine=false; 4 public Customer(String name){ 5 this.name=name; 6 } 7 @Override 8 public void update(Observable o, Object arg) { 9 System.out.println(name+" 拿到了新的杂志 "+arg);10 }11 }
Publisher类
1 import java.util.Observable; 2 public class Publisher extends Observable { 3 private String magazine; 4 public Publisher(){ 5 super(); 6 magazine=""; 7 } 8 public void publishNewMagazine(String magazine){ 9 setChanged();10 this.magazine=magazine;11 notifyObservers(magazine);12 }13 }
test代码完全不用改,输出如下
java给的观察者模式的实现方法其实还有有蛮多的局限性的,比如observable是个类而不是接口,而java是不支持多继承的,所以有了局限性,所以大多数情况下,我们还是自己实现会比较方便。
观察者模式到此结束♪(^∇^*)
参考资料:
1,《Head First 设计模式》
2,在线java API