设计模式(行为型)-观察者模式
目录
定义
类图
角色
Subject:抽象主题(抽象被观察者)
ConcreteSubject:具体主题(具体被观察者)
Observer:抽象观察者
ConcrereObserver:具体观察者
优缺点
优点
缺点
使用场景
代码示例
总结
定义
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象(被观察者)的状态发生改变时,所有依赖于它的对象(观察者)都将得到通知并被自动更新。这种模式又被称为发布 - 订阅(Publish/Subscribe)模式。它使得被观察者和观察者之间实现了松耦合,被观察者无需知道具体的观察者是谁,只需专注于自身状态的变化,而观察者则专注于对状态变化的响应。
例如,在一个新闻发布系统中,新闻机构是被观察者,而众多订阅该新闻机构的用户则是观察者。当新闻机构发布了一条新的新闻(状态改变),所有订阅的用户都会收到通知并更新自己的新闻列表(自动更新)。
类图
角色
-
Subject:抽象主题(抽象被观察者)
-
抽象主题角色负责管理所有观察者对象,它将所有观察者对象保存在一个集合里。每个主题都可以拥有任意数量的观察者。抽象主题提供了增加和删除观察者对象的接口,这样观察者可以动态地注册和注销对主题的观察。例如,在一个图形绘制系统中,图形对象可以作为抽象主题,而对图形状态感兴趣的各种监听器就是观察者。图形对象提供了添加和移除监听器的方法。
-
ConcreteSubject:具体主题(具体被观察者)
-
具体主题是抽象主题的具体实现。它负责将有关状态存入具体观察者对象。当具体主题的内部状态发生改变时,它会给所有注册过的观察者发送通知。比如在一个股票交易系统中,股票对象就是具体主题,当股票价格发生变化(内部状态改变)时,它会通知所有关注该股票的投资者(观察者)。
-
Observer:抽象观察者
-
抽象观察者是所有具体观察者的抽象类。它定义了一个更新接口,当观察者得到主题更改通知时,将通过这个接口来更新自己。例如,在一个天气监测系统中,各种显示天气信息的组件都可以看作是观察者,它们都实现了抽象观察者定义的更新接口,以便在天气数据(主题)发生变化时更新自己的显示内容。
-
ConcrereObserver:具体观察者
-
具体观察者实现了抽象观察者定义的更新接口。在得到主题更改通知时,具体观察者会根据通知内容更新自身的状态。比如在一个社交媒体平台中,用户的个人页面就是具体观察者,当用户发布了新动态(主题状态改变),关注该用户的其他用户的页面(具体观察者)会通过更新接口获取新动态并更新显示。
优缺点
优点
-
实现了观察者和被观察者之间的抽象耦合:被观察者只需要关注自身状态的变化,而无需关心具体的观察者是谁以及如何处理状态变化。观察者也只需实现统一的更新接口,不依赖于被观察者的具体实现。这使得系统的扩展性和维护性大大提高。例如,在一个游戏开发中,游戏角色(被观察者)的状态变化可以通知各种不同的游戏元素(观察者),如场景特效、音效等,而游戏角色无需知道这些具体的观察者细节。
-
动态联动:观察者可以根据需要动态地注册和注销对主题的观察。这意味着系统可以根据运行时的需求灵活地调整观察者和被观察者之间的关系。比如在一个实时监控系统中,新的监控设备(观察者)可以随时接入系统并开始观察特定的被监控对象(主题),而当设备故障或不再需要时也可以方便地注销。
-
广播通信:被观察者会向所有登记的观察者发出通知,这种广播机制使得信息能够高效地传播给所有相关的对象。在一个分布式系统中,一个节点(被观察者)的状态变化可以通过观察者模式快速通知到其他相关节点(观察者),实现系统的一致性和协同工作。
缺点
-
开发和调试复杂性:在应用观察者模式时,由于程序中包括一个被观察者和多个观察者,开发和调试的难度会增加。需要仔细处理观察者的注册、注销以及通知的顺序等问题,否则可能会出现意想不到的错误。例如,在一个大型电商系统中,订单状态的变化(被观察者)会通知多个模块(观察者),如库存管理、物流配送等,对这些复杂关系的调试需要花费更多的时间和精力。
-
运行效率问题:当观察者数量众多时,每次被观察者状态改变都要通知所有观察者,可能会导致性能下降。特别是在通知过程中如果存在复杂的业务逻辑,这种影响会更加明显。比如在一个社交网络平台中,一个热门用户的动态更新(被观察者)可能会通知成千上万的粉丝(观察者),如果处理不当,可能会造成系统响应变慢。此外,在一些编程语言中,消息通知默认是顺序执行的,一个观察者的卡顿会影响整体的执行效率。
使用场景
-
关联行为场景:当系统中存在多个对象之间存在关联行为,且这些行为是可拆分的,而不是 “组合” 关系时,适合使用观察者模式。例如,在一个音乐播放软件中,播放列表(被观察者)的变化(如添加、删除歌曲)会同时影响播放进度条、歌曲信息显示等多个组件(观察者),这些组件的行为是相互关联但又可独立变化的。
-
事件多级触发场景:在一些复杂的业务场景中,一个事件的发生可能会触发一系列的后续事件,观察者模式可以很好地处理这种多级触发的情况。比如在一个工作流管理系统中,任务状态的改变(被观察者)可能会触发审批流程、通知相关人员等多个后续事件(观察者),这些事件又可能进一步触发其他事件。
-
跨系统的消息交换场景:如消息队列、事件总线的处理机制中,观察者模式被广泛应用。不同的系统可以作为观察者订阅特定的消息主题(被观察者),当主题有新消息发布时,订阅的系统会收到通知并进行相应处理。例如,在一个微服务架构的电商系统中,订单服务(被观察者)可以通过消息队列向库存服务、支付服务等多个其他服务(观察者)发送订单状态变化的消息。
代码示例
以下是一个使用 Java 语言实现观察者模式的简单示例:
import java.util.ArrayList;
import java.util.List;
// 抽象主题
abstract class Subject {
private List<Observer> observers = new ArrayList<>();
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
public abstract int getState();
}
// 具体主题
class ConcreteSubject extends Subject {
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyObservers();
}
}
// 抽象观察者
interface Observer {
void update(Subject subject);
}
// 具体观察者
class ConcreteObserver implements Observer {
private int observerState;
@Override
public void update(Subject subject) {
observerState = ((ConcreteSubject) subject).getState();
System.out.println("观察者状态更新为: " + observerState);
}
}
在上述代码中,Subject类是抽象主题,定义了添加、删除观察者以及通知观察者的方法。ConcreteSubject是具体主题,实现了Subject的抽象方法,并在状态改变时通知观察者。Observer是抽象观察者接口,定义了更新方法。ConcreteObserver是具体观察者,实现了Observer接口的更新方法,根据主题的状态更新自身状态。
通过以下测试代码可以验证观察者模式的运行:
public class Main {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer = new ConcreteObserver();
subject.attach(observer);
subject.setState(10);
}
}
当subject的状态被设置为 10 时,会自动通知observer,observer会更新自身状态并输出相应信息。
总结
观察者模式作为一种强大的设计模式,通过定义对象间的一对多依赖关系,实现了对象之间的解耦,提高了系统的灵活性和可维护性。尽管在应用过程中需要注意开发和调试的复杂性以及运行效率问题,但在合适的场景下,观察者模式能够极大地提升软件系统的质量和可扩展性。无论是在小型应用还是大型分布式系统中,观察者模式都有着广泛的应用前景,是开发者在软件设计过程中不可或缺的工具之一。希望通过本文的介绍,读者能够对观察者模式有更深入的理解,并在实际项目中灵活运用这一模式。