观察者模式

Post on Oct 02, 2021 by Wei Lin

介绍

观察者模式(Observer Pattern)又称为发布-订阅模式,是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,其相关依赖对象都能够自动收到通知并进行相应的更新。


观察者模式的核心思想是将观察者对象注册到被观察者对象上,当被观察者对象的状态发生变化时,它会通知所有已注册的观察者对象进行更新。这种通知机制是基于松散耦合的,被观察者对象并不知道具体的观察者对象是谁,而观察者对象也不需要知道被观察者对象的细节,它们之间只通过定义好的接口进行通信。

模式角色

(1) 抽象主题(Subject)

定义了被观察者对象的接口,包含了添加、删除和通知观察者的方法。


(2) 具体主题(Concrete Subject)

实现了抽象主题接口,维护观察者对象的列表,并在状态发生变化时通知观察者。


(3) 抽象观察者(Observer)

定义了观察者对象的接口,包含了接收通知和进行更新的方法。


(4) 具体观察者(Concrete Observer)

实现了抽象观察者接口,具体实现观察者的更新方法,以便在接收到通知时进行相应的操作。

观察者模式.png

代码示例

package com.behavioral;

import java.util.ArrayList;
import java.util.List;

/**
 * 观察者(Observer)模式
 * 又称为发布-订阅模式
 */
public class ObserverPattern {
    public static void main(String[] args) {
        // 创建具体主题对象
        Subject subject = new ConcreteSubject();

        // 创建具体观察者对象
        Observer observer1 = new ConcreteObserver("observer1");
        Observer observer2 = new ConcreteObserver("observer2");

        // 注册观察者
        subject.registerObserver(observer1);
        subject.registerObserver(observer2);

        // 被观察者改变状态,并向已注册的观察者发布通知
        subject.setState(100);
        subject.notifyObservers();
    }
}

/**
 * 抽象主题(Subject)接口,用于注册、移除和通知观察者
 */
interface Subject {
    void registerObserver(Observer observer);

    void removeObserver(Observer observer);

    void notifyObservers();

    void setState(int state);

    int getState();
}

/**
 * 具体主题(ConcreteSubject)类,实现抽象主题接口,并维护观察者列表
 */
class ConcreteSubject implements Subject {
    private final List<Observer> observers = new ArrayList<>();
    private int state;

    @Override
    public void setState(int state) {
        this.state = state;
    }

    @Override
    public int getState() {
        return state;
    }

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(this);
        }
    }
}

/**
 * 抽象观察者(Observer)接口,用于接收通知和更新
 */
interface Observer {
    void update(ConcreteSubject subject);
}

/**
 * 具体观察者(ConcreteObserver)类,实现抽象观察者接口
 */
class ConcreteObserver implements Observer {
    private final String observerName;

    ConcreteObserver(String observerName) {
        this.observerName = observerName;
    }

    @Override
    public void update(ConcreteSubject subject) {
        int state = subject.getState();
        System.out.println(observerName + " get notification and subject state changed to: " + state);
    }
}

应用场景

(1) 通知多个对象

当一个对象的状态变化需要通知其他多个对象,并且这些对象的具体数量和身份可能在运行时发生变化时,可以使用观察者模式。例如,一个新闻发布者需要通知多个订阅者,或者一个股票交易系统需要通知多个观察者。


(2) 影响多个对象

当一个对象的改变需要影响其他对象,但是被影响的对象不需要知道是哪个对象对其进行了改变时,可以使用观察者模式。例如,一个温度传感器检测到温度变化后,通知给显示器和日志记录器,但这些对象不需要知道具体是哪个传感器发出的通知。


(3) 触发一系列操作

当一个对象的改变需要触发一系列的操作,而且这些操作可能位于不同的对象中,可以使用观察者模式。例如,一个订单状态发生变化后,需要更新库存、发送邮件通知和生成报表等操作。


(4) 多个依赖

当需要在对象间建立一种一对多的依赖关系,使得被观察者对象的改变能够通知到所有观察者对象时,可以使用观察者模式。例如,一个用户界面中的控件需要根据数据模型的变化进行更新。


优缺点

(1) 观察者模式的优点

  • 松散耦合:被观察者对象和观察者对象之间的耦合度低,它们可以独立变化,互不影响。
  • 可扩展性:可以方便地添加新的观察者对象和被观察者对象,扩展系统的功能。
  • 规范了对象间的关系:观察者模式明确了被观察者对象和观察者对象之间的关系,使系统更加清晰。
  • 支持广播通信:被观察者对象可以同时通知多个观察者对象,实现了一对多的通信方式。


(2) 观察者模式的缺点

  • 如果观察者对象较多或者观察者对象之间的处理逻辑复杂,可能会导致性能问题。
  • 观察者模式可能导致对象间的循环依赖,需要注意解决循环引用的问题。

Android中的应用-ContentObserver

ContentObserver即内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它,并执行相应的操作(如刷新页面)。


ContentObserver实现就是基于观察者模式的。使用到的对象如下:

  • ContentService:具体主题(Concrete Subject),当发现数据源变化时,会通知所有注册该数据源的ContentObserver;
  • ContentObserver:抽象观察者;
  • MyContentObserver:用户继承自ContentObserver的具体观察者,创建时需要指明被观察数据的uri,并且将自身注册到ContentService。


数据源的变化会由ContentProvider调用getContentResolver().notifyChange()通知到ContentService,之后再由ContentService调用到MyContentObserver。