本文主要讲解OpenJDK观察者模式的2个工具类,java.util.Observer观察者接口,java.util.Observable被观察者基类。
然后,给出了一个常见的观察者应用示例。
Observer观察者接口
/** * 一个类可以实现Observer接口,当它想要得到“被观察者”对象变化通知的时候 */ public interface Observer { /** * 当被观察的对象发生变化时,这个方法被调用。一个应用调用Observable对象的notifyObservers方法,去通知所有的观察者, Observable对象发生了变化。 * @param o 被观察的对象. * @param arg 传递给notifyObservers方法的参数. */ void update(Observable o, Object arg); }
Observable被观察者基类
这个类代表一个可以观察的对象,或数据(在模型-视图范式中)。
它能够被继承去代表一个应用想要观察的对象。
一个observable对象可以有一个或多个观察者。
一个观察者可以是一个实现了Observer接口的对象。
当一个observable实例发生了变化,一个应用可以调用Observable的notifyObservers方法,去通知
它的观察者所发生的变化,通过调用观察者的update方法。
通知被传递的顺序是没有指定的。
在Observable类中的默认实现是,按注册顺序通知Observers。
但是子类可以改变这种顺序,使用没有保证的顺序,在分开的线程中传递通知,或者确保他们的子类遵循他们所选择的这个顺序。
注意:这个通知机制和线程没有任何关系,与Object对象的wait和notify机制没有任何关系。
当一个observable被新创建的时候,它的observer集合是空的。
两个observer被认为是同一个,当且仅当他们的equals方法比较返回true的时候。
public class Observable { //该对象是否已经发生了变化,默认为false private boolean changed = false; //Vector是线程安全的,ArrayList是非线程安全的 private Vector obs; //构造一个没有观察者的Observable对象 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) { //我们不想要Observer 回调任何代码,在保持它的Monitor的时候。 //从Vector中提取Observable 和存储Observer的状态的代码需要同步,但是通知观察者却不需要。 //最坏的潜在竞争性条件结果是: //1.一个新增加的观察者将会错误正在进行的通知 //2.一个最近被取消注册的观察者将会被错误的通知,当它不需要关心的时候 if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } //通知观察者的代码,在同步块之外 for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } //删除所有的观察者 public synchronized void deleteObservers() { obs.removeAllElements(); } //表明Observable对象已经发生了变化 protected synchronized void setChanged() { changed = true; } //表明这个对象已经不再变化,或者说明已经通知到所有的观察者最近的一次变化。 protected synchronized void clearChanged() { changed = false; } //测试该对象是否已经发生了变化。当且仅当setChanged方法比clearChanged方法调用的次数更多的时候,返回true。 public synchronized boolean hasChanged() { return changed; } // 返回Observable对象的观察者的数量. public synchronized int countObservers() { return obs.size(); } }
代码示例
书,被观察者(或者称之为订阅主题)
import java.util.Observable; //书,被观察者(或者称之为订阅主题) public class Book extends Observable { // 书的ID private Integer id; // 书名 private String name; // 当前价格 private Double price; // 当书的价格修改时,调用该方法 public void modifyPrice(Double newPrice) { ChangeStatus status = null; // 差值大于0.01,就认为价格放生了变化 if (Math.abs(newPrice - price) >= 0.01) { setChanged(); status = new ChangeStatus(); status.setId(id); status.setName(name); status.setOldPrice(price); status.setNewPrice(newPrice); } // 通知客户书已经降价 notifyObservers(status); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } }
观察者:用户的邮箱
import java.util.Observable; import java.util.Observer; /** * 观察者:用户的邮箱 */ public class BuyerEmail implements Observer { // 该方法会被“被观察者的父类”即Observable调用 public void update(Observable o, Object arg) { // 这里做具体的发电子邮件的程序 if (arg instanceof ChangeStatus) { NotifyUtils.sendEmail((ChangeStatus) arg); } } }
观察者:用户的手机
import java.util.Observable; import java.util.Observer; /** * 观察者:用户的手机 */ public class BuyerMobile implements Observer { // 该方法会被“被观察者的父类”即Observable调用 public void update(Observable o, Object arg) { // 这里做具体的发电子邮件的程序 if(arg instanceof ChangeStatus){ NotifyUtils.sendSMS((ChangeStatus)arg); } } }
一本书的信息变化的实体类
public class ChangeStatus { //书的ID private Integer id; //书名 private String name; //过去的价格 private Double oldPrice; //最新的价格 private Double newPrice; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Double getOldPrice() { return oldPrice; } public void setOldPrice(Double oldPrice) { this.oldPrice = oldPrice; } public Double getNewPrice() { return newPrice; } public void setNewPrice(Double newPrice) { this.newPrice = newPrice; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
发送通知的工具类
public class NotifyUtils { // 发送邮件通知用户 public static void sendEmail(ChangeStatus status) { print(status); } // 发送短信通知用户 public static void sendSMS(ChangeStatus status) { print(status); } // 一种只是用来作为“演示”的发送信息的方法 private static void print(ChangeStatus status) { println("*******************************************************"); String messageContent = messageContent(status); println(messageContent); println("********************************************************"); } private static String messageContent(ChangeStatus status) { // 一般,降价才会通知,这里只当是降价了 String str = "尊敬的用户,您好!\n"; str += "您收藏的图书《" + status.getName() + "》降价了。\n"; str += "过去的价格是:" + status.getOldPrice() + "\n"; str += "现在的价格是:" + status.getNewPrice() + "\n"; str += "您可以通过以下链接 http://blog.csdn.net/FansUnion 进行购买。"; return str; } private static void println(Object content) { System.out.println(content); } }
程序入口
public class ObserverPatternTest { /** * 观察者模式-用法示例 */ public static void main(String[] args) { Book book = new Book(); book.setName("中国象棋程序的设计与实现"); book.setPrice(45.0); // 下面的观察者在实际应用中可以从数据库或文件中读取 BuyerEmail email = new BuyerEmail(); BuyerMobile mobile = new BuyerMobile(); // 增加观察者,在实际应用中就是哪些人对该书作了关注 book.addObserver(email); book.addObserver(mobile); book.modifyPrice(34.00); } }
输出结果
*******************************************************
尊敬的用户,您好!
您收藏的图书《中国象棋程序的设计与实现》降价了。
过去的价格是:45.0
现在的价格是:34.0
您可以通过以下链接 http://blog.csdn.net/FansUnion 进行购买。
********************************************************
*******************************************************
尊敬的用户,您好!
您收藏的图书《中国象棋程序的设计与实现》降价了。
过去的价格是:45.0
现在的价格是:34.0
您可以通过以下链接 http://blog.csdn.net/FansUnion 进行购买。
********************************************************
参考资料:OpenJDK7源码
原文参见: http://FansUnion.cn/articles/2936
版权声明:本文为博主原创文章,未经博主允许不得转载。