「补课」进行时:设计模式(15)——观察者模式
1. 前文汇总
2. 观察者模式
2.1 定义
观察者模式(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.(定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。)
2.2 通用类图
Subject 被观察者: 定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。 ConcreteSubject 具体的被观察者: 定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。 Observer 观察者: 观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。 ConcreteObserver 具体的观察者: 每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。
2.3 通用代码
Subject 被观察者:
public abstract class Subject {
// 定义一个观察者数组
private Vector 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 观察者:
public interface Observer {
void update();
}
ConcreteObserver 具体的观察者:
public class ConcreteObserver implements Observer{
@Override
public void update() {
System.out.println("进行消息处理");
}
}
测试场景类:
public class Test {
public static void main(String[] args) {
// 创建一个被观察者
ConcreteSubject subject = new ConcreteSubject();
// 创建一个观察者
Observer observer = new ConcreteObserver();
// 观察者观察被观察者
subject.addObserver(observer);
// 观察者开始活动了
subject.doSomething();
}
}
3. 一个案例
观察者模式是设计模式中的超级模式,有关他的应用随处可见。
就比如说微信公众号,我每天推送一篇博文内容,订阅的用户都能够在我发布推送之后及时接收到推送,方便地在手机端进行阅读。
订阅者接口(观察者)
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 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("设计模式 | 单例模式及典型应用", "单例模式的内容....");
}
}
测试结果
<极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 观察者模式及典型应用>,内容为 <观察者模式的内容...>
用户<张三> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
<极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 单例模式及典型应用>,内容为 <单例模式的内容....>
用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
4. JDK 对的观察者模式的支持
观察者模式在 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 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("设计模式 | 单例模式及典型应用", "单例模式的内容....");
}
}
测试结果:
<极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 观察者模式及典型应用>,内容为 <观察者模式的内容...>
用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
用户<张三> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 观察者模式及典型应用>
<极客挖掘机>微信公众号 发布了一篇推送,文章名称为 <设计模式 | 单例模式及典型应用>,内容为 <单例模式的内容....>
用户<王五> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
用户<李四> 接收到 <极客挖掘机>微信公众号 的推送,文章标题为 <设计模式 | 单例模式及典型应用>
和前面的示例结果完全一致。