Skip to main content

Observer 观察者

Intent

Observer is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.

又被称为发布-订阅 Publish/Subscribe 模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己

Problem

假设有两种类型的对象: Customer and Store. 顾客可以每天来商店看看产品是否到货。但如果商品尚未到货时,绝大多数来到商店的顾客都会空手而归。

另一方面,每次新产品到货时,商店可以向所有顾客发送邮件 (可能会被视为垃圾邮件)。这样,部分顾客就无需反复前往商店了,但也可能会惹恼对新产品没有兴趣的其他顾客。

我们似乎遇到了一个矛盾: 要么让顾客浪费时间检查产品是否到货,要么让商店浪费资源去通知没有需求的顾客。

Solution

The object that has some interesting state is often called subject, but since it’s also going to notify other objects about the changes to its state, we’ll call it publisher. All other objects that want to track changes to the publisher’s state are called subscribers.

The Observer pattern suggests that you add a subscription mechanism to the publisher class so individual objects can subscribe to or unsubscribe from a stream of events coming from that publisher. 观察者模式建议你为发布者类添加订阅机制,让每个对象都能订阅或取消订阅发布者事件流。

  • an array field for storing a list of references to subscriber objects. 一个用于存储订阅者对象引用的列表成员变量
  • several public methods which allow adding subscribers to and removing them from that list. 几个用于添加或删除该列表中订阅者的公有方法

Now, whenever an important event happens to the publisher, it goes over its subscribers and calls the specific notification method on their objects. 无论何时发生了重要的发布者事件, 它都要遍历订阅者并调用其对象的特定通知方法。

Real apps might have dozens of different subscriber classes that are interested in tracking events of the same publisher class. You wouldn’t want to couple the publisher to all of those classes. Besides, you might not even know about some of them beforehand if your publisher class is supposed to be used by other people. 实际应用中可能会有十几个不同的订阅者类跟踪着同一个发布者类的事件,你不会希望发布者与所有这些类相耦合的。此外如果他人会使用发布者类,那么你甚至可能会对其中的一些类一无所知。

因此,所有订阅者都必须实现同样的接口,发布者仅通过该接口与订阅者交互。接口中必须声明通知方法及其参数, 这样发布者在发出通知时还能传递一些上下文数据。

如果你的应用中有多个不同类型的发布者,且希望订阅者可兼容所有发布者,那么你甚至可以进一步让所有订阅者遵循同样的接口。该接口仅需描述几个订阅方法即可。这样订阅者就能在不与具体发布者类耦合的情况下通过接口观察发布者的状态。

Real-World Analogy

如果你订阅了一份杂志或报纸,那就不需要再去报摊查询新出版的刊物了。publisher 会在刊物出版后直接将最新一期寄送至你的邮箱中。

publisher 负责维护subscribers列表,了解subscribers对哪些刊物感兴趣。当subscribers希望publisher停止寄送新一期的杂志时,他们可随时从该列表中退出。

Structure

  1. The Publisher issues events of interest to other objects. These events occur when the publisher changes its state or executes some behaviors. Publishers contain a subscription infrastructure that lets new subscribers join and current subscribers leave the list.

  2. When a new event happens, the publisher goes over the subscription list and calls the notification method declared in the subscriber interface on each subscriber object. 当新事件发生时,发送者会遍历订阅列表并调用每个订阅者对象的通知方法。

  3. The Subscriber interface declares the notification interface. In most cases, it consists of a single update method. The method may have several parameters that let the publisher pass some event details along with the update. 在绝大多数情况下, 该接口仅包含一个 update 方法

  4. Concrete Subscribers perform some actions in response to notifications issued by the publisher. All of these classes must implement the same interface so the publisher isn’t coupled to concrete classes. 具体订阅者 可以执行一些操作来回应发布者的通知。所有具体订阅者类都实现了同样的接口,因此发布者不需要与具体类相耦合。

  5. Usually, subscribers need some contextual information to handle the update correctly. For this reason, publishers often pass some context data as arguments of the notification method. The publisher can pass itself as an argument, letting subscriber fetch any required data directly. 订阅者通常需要一些上下文信息来正确地处理更新。因此,发布者通常会将一些上下文数据作为通知方法的参数进行传递。发布者也可将自身作为参数进行传递,使订阅者直接获取所需的数据。

  6. The Client creates publisher and subscriber objects separately and then registers subscribers for publisher updates.

Implementations

当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了某个公众号。

定义抽象观察者类,里面定义一个更新的方法

Observer.java
public interface Observer {
void update(String message);
}

定义具体观察者类,微信用户是观察者,里面实现了更新的方法

WechatUser.java
public class WechatUser implements Observer {
private String name;

public WechatUser(String name) {
this.name = name;
}

@Override
public void update(String message) {
System.out.println(name + "-" + message);
}
}

定义抽象对象类,提供了 attach, detach, notify 三个方法

Subject.java
public interface Subject {
// 添加订阅者对象
void attach(Observer observer);
void detach(Observer observer);
void notify(String message);
}

微信公众号是具体对象 (具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法

SubscriptionSubject.java
public class SubscriptionSubject implements Subject {
// 定义一个集合,用于存储多个观察者对象
private List<Observer> wechatUserList = new ArrayList<>();

@Override
public void attach(Observer observer) {
wechatUserList.add(observer);
}

@Override
public void detach(Observer observer) {
wechatUserList.remove(observer);
}

@Override
public void notify(String message) {
// 遍历集合
for (Observer observer : wechatUserList) {
// 调用观察者对象中的方法
observer.update(message);
}
}
}

Client

Client.java
public class Client {
public static void main(String[] args) {
// 创建公众号对象
SubscriptionSubject subject = new SubscriptionSubject();

// 订阅公众号
subject.attach(new WechatUser("a"));
subject.attach(new WechatUser("b"));
subject.attach(new WechatUser("c"));

// 公众号更新,发出消息给订阅者 (观察者对象)
subject.notify("新文章发布了");
}
}