目录
一、什么是观察者模式
观察者模式(Observer Pattern),是一种行为型设计模式,也被称为发布 - 订阅模式(Publish-Subscribe Pattern) 。这种模式定义了对象之间的一种一对多依赖关系,使得每当一个对象(被观察者)的状态发生改变时,其所有依赖于它的对象(观察者)都会得到通知并被自动更新。
打个比方,就拿我们日常使用的公众号来说。当你关注了一个公众号,这个公众号就相当于被观察者,而你以及其他关注这个公众号的粉丝们就是观察者。一旦公众号发布了新文章(也就是状态发生改变),平台就会自动通知到所有关注它的粉丝(所有观察者收到通知并更新,比如在各自的微信端收到推送消息)。再比如,在股票交易系统中,每只股票就是被观察者,而股民们就是观察者。当某只股票的价格发生变化时,关注这只股票的股民就会收到通知,进而可以根据价格变化做出相应的决策。
二、观察者模式的组成
观察者模式主要包含四个核心角色:主题(Subject)、具体主题(Concrete Subject)、观察者(Observer)和具体观察者(Concrete Observer)。下面我将为大家一一介绍。
2.1 主题(Subject)
主题也被称为抽象目标类,它是一个接口或者抽象类 ,定义了一系列管理观察者的方法,是整个观察者模式的核心纽带。其主要作用是提供一个统一的管理观察者的接口,使得具体主题类可以方便地添加、删除和通知观察者。在主题接口或抽象类中,通常会包含以下方法:
-
注册观察者方法:用于将观察者对象添加到主题的观察者列表中,使主题能够知道有哪些观察者对它的状态变化感兴趣。比如在前面提到的公众号例子中,公众号提供的 “关注” 功能就相当于注册观察者的方法,用户通过关注公众号,将自己注册为观察者。
-
注销观察者方法:与注册方法相反,用于将不再感兴趣的观察者从观察者列表中移除。例如公众号的 “取消关注” 功能,用户取消关注公众号,就相当于从公众号的观察者列表中注销自己。
-
通知观察者方法:当主题的状态发生变化时,调用此方法通知所有注册的观察者,告知它们主题状态已改变,以便观察者做出相应的处理。如公众号发布新文章后,会通过通知方法向所有关注它的用户发送推送通知。
2.2 具体主题(Concrete Subject)
具体主题类是主题接口的具体实现,它负责维护一个观察者列表,存储所有对其感兴趣的观察者对象 。当具体主题的内部状态发生改变时,它会遍历观察者列表,调用每个观察者的更新方法,通知它们状态已改变。
以股票交易系统为例,某只股票就是一个具体主题。它会维护一个包含所有关注这只股票股民(观察者)的列表。当股票价格发生变化时(状态改变),股票这个具体主题就会通知列表中的所有股民,告知他们股票价格的变动情况,股民们(观察者)则可以根据这个通知做出买卖股票的决策。在具体实现中,具体主题类通常会在其内部定义一个用于存储观察者对象的集合,比如 Java 中的ArrayList,然后实现主题接口中定义的注册、注销和通知观察者的方法。
2.3 观察者(Observer)
观察者是一个接口或者抽象类,它为所有具体观察者定义了一个统一的更新接口 。这个接口的主要作用是让具体观察者实现一个方法,当它们接收到主题的通知时,能够执行相应的操作来更新自身的状态,以与主题的状态保持同步。在观察者接口或抽象类中,一般只定义一个核心的更新方法,这个方法通常会接收主题传递过来的一些状态信息,以便观察者根据这些信息进行自身状态的更新。
例如在一个游戏场景中,玩家角色是观察者,游戏中的各种事件(如怪物出现、道具刷新等)是主题。玩家角色实现了观察者接口,当怪物出现这个主题发生状态变化(怪物出现了)时,玩家角色作为观察者,会通过实现的更新方法来做出反应,比如准备战斗、调整策略等。
2.4 具体观察者(Concrete Observer)
具体观察者类实现了观察者接口,它是真正对主题状态变化感兴趣并做出具体响应的对象 。每个具体观察者都有自己独特的业务逻辑,当接收到主题的通知时,它们会根据自身的需求和逻辑,利用主题传递过来的信息来更新自己的状态或者执行特定的操作,从而与主题的状态保持一致。
继续以上述游戏场景为例,游戏中的不同玩家就是具体观察者。当怪物出现的通知传来时,战士玩家可能会选择直接冲上去攻击怪物;法师玩家则可能会开始吟唱法术,准备远程攻击;而辅助玩家可能会给队友施加增益状态,以提高团队的战斗力。这些不同的玩家(具体观察者)根据自己的角色特点和游戏策略,对怪物出现(主题状态变化)做出了不同的响应,实现了各自独特的业务逻辑。
三、观察者模式工作流程
了解了观察者模式的组成部分后,接下来我们深入探讨一下它的工作流程,看看各个角色在其中是如何协同工作的。
3.1 注册观察者
当观察者对主题的状态变化感兴趣时,它会调用主题提供的注册方法,将自己添加到主题的观察者列表中 。这个过程就好比我们在电商平台上关注了某个商品,我们(观察者)通过点击 “关注” 按钮(调用注册方法),将自己的信息(观察者对象)添加到该商品(主题)的关注列表中。在代码实现上,以 Java 语言为例,假设我们有一个Subject接口和一个ConcreteSubject类,以及一个Observer接口和一个ConcreteObserver类:
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observe