「补课」进行时:设计模式系列java
观察者模式(Observer Pattern)也叫作发布订阅模式(Publish/subscribe),它是一个在项目中常常使用的模式,其定义以下:设计模式
Define a one-to-many dependency between objects so that when oneobject changes state,all its dependents are notified and updatedautomatically.(定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则全部依赖于它的对象都会获得通知并被自动更新。)数组
Subject 被观察者:微信
public abstract class Subject { // 定义一个观察者数组 private Vector<Observer> obsVector = new Vector<>(); // 添加一个观察者 public void addObserver(Observer obsVector) { this.obsVector.add(obsVector); } // 删除一个观察者 public void delObserver(Observer observer) { this.obsVector.remove(observer); } // 通知全部观察者 public void notifyObservers() { for (Observer obs : this.obsVector) { obs.update(); } } }
ConcreteSubject 具体的被观察者:并发
public class ConcreteSubject extends Subject { public void doSomething() { // 具体的业务 super.notifyObservers(); } }
Observer 观察者:ide
public interface Observer { void update(); }
ConcreteObserver 具体的观察者:测试
public class ConcreteObserver implements Observer{ @Override public void update() { System.out.println("进行消息处理"); } }
测试场景类:this
public class Test { public static void main(String[] args) { // 建立一个被观察者 ConcreteSubject subject = new ConcreteSubject(); // 建立一个观察者 Observer observer = new ConcreteObserver(); // 观察者观察被观察者 subject.addObserver(observer); // 观察者开始活动了 subject.doSomething(); } }
观察者模式是设计模式中的超级模式,有关他的应用随处可见。设计
就好比说微信公众号,我天天推送一篇博文内容,订阅的用户都可以在我发布推送以后及时接收到推送,方便地在手机端进行阅读。3d
订阅者接口(观察者)
public interface Subscriber { void receive(String publisher, String articleName); }
微信客户端(具体观察者)
public class WeChatClient implements Subscriber { private String username; public WeChatClient(String username) { this.username = username; } @Override public void receive(String publisher, String articleName) { System.out.println(String.format("用户<%s> 接收到 <%s>微信公众号 的推送,文章标题为 <%s>", username, publisher, articleName)); } }
一个微信客户端(具体观察者)
public class Publisher { private List<Subscriber> subscribers; private boolean pubStatus = false; public Publisher() { subscribers = new ArrayList<>(); } protected void subscribe(Subscriber subscriber) { this.subscribers.add(subscriber); } protected void unsubscribe(Subscriber subscriber) { if (this.subscribers.contains(subscriber)) { this.subscribers.remove(subscriber); } } protected void notifySubscribers(String publisher, String articleName) { if (this.pubStatus == false) { return; } for (Subscriber subscriber : this.subscribers) { subscriber.receive(publisher, articleName); } this.clearPubStatus(); } protected void setPubStatus() { this.pubStatus = true; } protected void clearPubStatus() { this.pubStatus = false; } }
具体目标
public class WeChatAccounts extends Publisher { private String name; public WeChatAccounts(String name) { this.name = name; } public void publishArticles(String articleName, String content) { System.out.println(String.format("\n<%s>微信公众号 发布了一篇推送,文章名称为 <%s>,内容为 <%s> ", this.name, articleName, content)); setPubStatus(); notifySubscribers(this.name, articleName); } }
测试类
public class Test { public static void main(String[] args) { WeChatAccounts accounts = new WeChatAccounts("极客挖掘机"); WeChatClient user1 = new WeChatClient("张三"); WeChatClient user2 = new WeChatClient("李四"); WeChatClient user3 = new WeChatClient("王五"); accounts.subscribe(user1); accounts.subscribe(user2); accounts.subscribe(user3); accounts.publishArticles("设计模式 | 观察者模式及典型应用", "观察者模式的内容..."); accounts.unsubscribe(user1); accounts.publishArticles("设计模式 | 单例模式及典型应用", "单例模式的内容...."); } }
测试结果
<极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 观察者模式及典型应用>,内容为 <观察者模式的内容...> 用户<张三> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用> 用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用> 用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用> <极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 单例模式及典型应用>,内容为 <单例模式的内容....> 用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用> 用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
观察者模式在 Java 语言中的地位很是重要。在 JDK 的 java.util
包中,提供了 Observable
类以及 Observer
接口,它们构成了JDK对观察者模式的支持。
在 java.util.Observer
接口中,仅有一个 update(Observable o, Object arg)
方法,当观察目标发生变化时被调用:
public interface Observer { void update(Observable o, Object arg); }
java.util.Observable
类则为目标类:
public class Observable { private boolean changed = false; private Vector<Observer> obs; 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); } // 通知方法,用于在方法内部循环调用向量中每个观察者的update()方法 public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { 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(); } // 该方法被调用后会设置一个boolean类型的内部标记变量changed的值为true,表示观察目标对象的状态发生了变化 protected synchronized void setChanged() { changed = true; } // 用于将changed变量的值设为false,表示对象状态再也不发生改变或者已经通知了全部的观察者对象,调用了它们的update()方法 protected synchronized void clearChanged() { changed = false; } // 返回对象状态是否改变 public synchronized boolean hasChanged() { return changed; } // 返回向量中观察者的数量 public synchronized int countObservers() { return obs.size(); } }
相比较咱们本身的示例 Publisher
, java.util.Observer
中多了并发和 NPE 方面的考虑 。
使用 JDK 对观察者模式的支持,改写一下上面的示例:
增长一个通知类 WechatNotice
,用于推送通知的传递:
public class WechatNotice { private String publisher; private String articleName; // 省略 get/set }
而后改写 WeChatClient
和 WeChatAccounts
,分别实现 JDK 的 Observer
接口和继承 Observable
类:
public class WeChatClient implements Observer { private String username; public WeChatClient(String username) { this.username = username; } @Override public void update(Observable o, Object arg) { WechatNotice notice = (WechatNotice) arg; System.out.println(String.format("用户<%s> 接收到 <%s>微信公众号 的推送,文章标题为 <%s>", username, notice.getPublisher(), notice.getArticleName())); } } public class WeChatAccounts extends Observable { private String name; public WeChatAccounts(String name) { this.name = name; } public void publishArticles(String articleName, String content) { System.out.println(String.format("\n<%s>微信公众号 发布了一篇推送,文章名称为 <%s>,内容为 <%s> ", this.name, articleName, content)); setChanged(); notifyObservers(new WechatNotice(this.name, articleName)); } }
最后是一个测试类:
public class Test { public static void main(String[] args) { WeChatAccounts accounts = new WeChatAccounts("极客挖掘机"); WeChatClient user1 = new WeChatClient("张三"); WeChatClient user2 = new WeChatClient("李四"); WeChatClient user3 = new WeChatClient("王五"); accounts.addObserver(user1); accounts.addObserver(user2); accounts.addObserver(user3); accounts.publishArticles("设计模式 | 观察者模式及典型应用", "观察者模式的内容..."); accounts.deleteObserver(user1); accounts.publishArticles("设计模式 | 单例模式及典型应用", "单例模式的内容...."); } }
测试结果:
<极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 观察者模式及典型应用>,内容为 <观察者模式的内容...> 用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用> 用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用> 用户<张三> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用> <极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 单例模式及典型应用>,内容为 <单例模式的内容....> 用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用> 用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
和前面的示例结果彻底一致。