13.观察者模式设计思想
目录介绍
- 01.观察者模式基础
- 1.1 观察者模式由来
- 1.2 观察者模式定义
- 1.3 观察者模式场景
- 1.4 观察者模式思考
- 02.观察者模式原理与实现
- 2.1 罗列一个场景
- 2.2 用例子理解观察者
- 2.3 案例演变分析
- 2.4 观察者模式基本实现
- 03.观察者模式分析
- 3.1 观察者模式官方案例
- 3.2 观察者模式结构图
- 3.3 观察者模式时序图
- 04.观察者模式优缺点
- 4.1 优点分析
- 4.2 缺点分析
- 4.3 符合设计原则分析
- 05.观察者模式使用
- 5.1 适用环境分析
01.观察者模式基础
1.1 观察者模式由来
- 建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。
- 在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系。可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机。
- 主要解决什么问题呢?
- 一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
1.2 观察者模式定义
- 观察者模式(Observer Pattern)的定义:
- 定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
1.3 观察者模式场景
- 观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。
- 在 GoF 的《设计模式》一书中,它的定义是这样的:Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
- 翻译成中文就是:在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。
- 一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。
- 在实际的项目开发中,这两种对象的称呼是比较灵活的,有各种不同的叫法,比如:Subject-Observer、Publisher-Subscriber、Producer-Consumer、EventEmitter-EventListener、Dispatcher-Listener。
- 只要应用场景符合刚刚给出的定义,都可以看作观察者模式。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
1.4 观察者模式思考
02.观察者模式原理与实现
2.1 罗列一个场景
- 微信公众号。在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。
- 我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是观察对象,有多个的微信用户关注了程序猿这个公众号。
- 如何何时使用观察者模式?
- 一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。关键代码:在抽象类里有一个 ArrayList 存放观察者们。
2.2 用例子理解观察者
- 大概实现的思路是这样:
- 第一步:抽象观察者
- 第二步:具体观察者
- 第三步:观察对象
- 第四步:具体观察对象
- 第五步:测试,创建公众号对象,然后添加用户订阅公众号,公众号更新
- 第一步:抽象观察者
public interface Observer { void update(String message); }
- 第二步:具体观察者
//具体的观察者角色类 public class WeiXinObserver implements Observer{ private String name; public WeiXinObserver(String name) { this.name = name; } @Override public void update(String message) { System.out.println(name + ": " + message); } }
- 第三步:观察对象
//观察对象 public interface Subject { //添加订阅者(观察者对象) void attach(Observer observer); //删除订阅者 void detach(Observer observer); //通知订阅者更新消息 void notify(String message); }
- 第四步:具体观察对象
//具体主题角色类 public class SubscriptionSubject implements Subject{ //定义一个集合,用来存储多个观察者对象 private List<Observer> weiXinUserList = new ArrayList<>(); @Override public void attach(Observer observer) { weiXinUserList.add(observer); } @Override public void detach(Observer observer) { weiXinUserList.remove(observer); } @Override public void notify(String message) { //遍历集合 for (Observer observer : weiXinUserList) { //调用观察者对象中的 update 方法 observer.update(message); } } }
- 第五步:测试,创建公众号对象,然后添加用户订阅公众号,公众号更新
private void test1() { //1.创建公众号对象 SubscriptionSubject subject = new SubscriptionSubject(); //2.订阅公众号 subject.attach(new WeiXinObserver("打工充")); subject.attach(new WeiXinObserver("心怡宝")); subject.attach(new WeiXinObserver("逗比果")); //3.公众号更新 subject.notify("打工充 设计模式专栏更新了!"); }
- 输出结果更新如下:
打工充: 打工充 设计模式专栏更新了! 心怡宝: 打工充 设计模式专栏更新了! 逗比果: 打工充 设计模式专栏更新了!
2.3 案例演变分析
2.4 观察者模式基本实现
- 在观察者模式中有如下角色:
- Subject:观察对象,定义了注册观察者和删除观察者的方法。此外,它还声明了“获取现在的状态”的方法。
- SubscriptionSubject:具体观察对象,当自身状态发生变化后,它会通知所有已经注册的 Observer 角色。
- Observer:抽象观察者,负责接收来自 Subject 角色的状态变化的通知,为此,它声明了 update 方法。
- WeiXinObserver:具体观察者,当它的 update 方法被调用后,会去获取要观察的对象的最新状态。
- 根据上面微信公众号的案例,绘制UML图如下
image
03.观察者模式分析
3.1 观察者模式官方案例
- 定义主题接口(Subject):主题接口定义了注册、注销和通知观察者的方法。
public interface Subject { void registerObserver(Observer observer); void unregisterObserver(Observer observer); void notifyObservers(); }
- 实现具体主题类(ConcreteSubject):具体主题类实现了主题接口,并维护一个观察者列表,用于注册、注销和通知观察者。
import java.util.ArrayList; import java.util.List; public class ConcreteSubject implements Subject { private List<Observer> observers = new ArrayList<>(); @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void unregisterObserver(Observer observer) { observers.remove(observer); } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(); } } }
- 定义观察者接口(Observer):观察者接口定义了接收主题通知的方法。
public interface Observer { void update(); }
- 实现具体观察者类(ConcreteObserver):具体观察者类实现了观察者接口,并在接收到主题通知时执行相应的操作。
public class ConcreteObserver implements Observer { @Override public void update() { // 执行相应的操作 } } //观察者1 public class ConcreteObserverOne implements Observer { @Override public void update() { //TODO: 获取消息通知,执行自己的逻辑... System.out.println("ConcreteObserverOne is notified."); } } //观察者2 public class ConcreteObserverTwo implements Observer { @Override public void update() { //TODO: 获取消息通知,执行自己的逻辑... System.out.println("ConcreteObserverTwo is notified."); } }
- 实际上,上面的代码算是观察者模式的“模板代码”,只能反映大体的设计思路。
- 在真实的软件开发中,并不需要照搬上面的模板代码。观察者模式的实现方法各式各样,函数、类的命名等会根据业务场景的不同有很大的差别,万变不离其宗,设计思路都是差不多的。
3.2 观察者模式结构图
- 观察者模式包含如下角色:
- Subject: 目标
- ConcreteSubject: 具体目标
- Observer: 观察者
- ConcreteObserver: 具体观察者
- 观察者模式结构图如下所示
image
3.3 观察者模式时序图
- 观察者模式时序图如下所示:
image
04.观察者模式优缺点
4.1 优点分析
- 优点:
- 1、观察者和被观察者解耦,增强了灵活性。
- 2、符合开闭原则,容易扩展。
- 3、支持广播通信,一个对象状态变化会通知多个观察者对象。
- 4、建立一套触发机制。
- 1、观察者和被观察者解耦,增强了灵活性。
- 观察者模式可以帮助实现业务解耦的原因在于它将主题对象和观察者对象解耦,使它们之间的依赖关系变得松散。
- 主题对象(也称为被观察者或发布者)和观察者对象(也称为订阅者)之间通过接口进行通信,而不是直接引用彼此。
- 主题对象和观察者对象可以独立地进行修改和扩展,而不会相互影响。
4.2 缺点分析
- 缺点
- 1、如果观察者很多,通知的开销很大。
- 2、被观察者发送通知,无法知道有哪些观察者处理。
- 观察者模式的缺点也有一些
- 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
4.3 符合设计原则分析
- 符合设计原则分析如下所示
- 1、单一职责原则(Single Responsibility Principle) :观察者和被观察者职责明确区分,都仅负责自己的功能。
- 2、开闭原则(Open Closed Principle) :可以新增观察者而不影响被观察者,扩展开放。
- 3、里氏替换原则(Liskov Substitution Principle) :观察者都遵循统一接口,扩展观察者不会对系统造成影响。
- 4、依赖倒转原则(Dependency Inversion Principle) :被观察者和观察者都依赖于抽象接口,不依赖具体实现。
- 5、接口隔离原则(Interface Segregation Principle) :观察者接口只定义了更新接口,避免了冗余。
05.观察者模式使用
5.1 适用环境分析
- 在以下情况下可以使用观察者模式:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。