跳转至

观察者模式

约 1080 个字 452 行代码 2 张图片 预计阅读时间 11 分钟

场景:办公室看股票

同事们上班偷偷看股票,请前台童子喆在老板回来时 打电话通知。某日老板带童子喆去拿材料,来不及通知,背对门的魏关姹大喊「我的股票涨停了哦」——被老板当场听到。大鸟指出:这正是 观察者模式 的典型场景。

什么是观察者模式

观察者模式(Observer,又称发布-订阅模式)属于 行为型 模式,定义 一对多 的依赖关系:多个观察者监听一个主题,主题状态变化时 自动通知 所有观察者并更新。

  • Subject(主题):被观察对象,维护观察者列表,状态变化时通知。
  • Observer(观察者):订阅主题,收到通知后执行相应动作(如关闭股票软件)。

观察者模式类图

初版代码中,前台类与看股票同事 双向耦合——若还要观察 NBA 直播,前台类就要大改。应运用 开放-封闭依赖倒转:双方都依赖 抽象Subject / Observer 接口,而非具体类。

使用观察者模式将主题与观察者 解耦,可动态增删观察者,系统更易扩展与维护。

观察者模式的结构

观察者模式一般有 4 个组成部分:

  • 主题Subject, 一般会定义成一个接口,提供方法用于 注册、删除和通知观察者 ,通常也包含一个状态,当状态发生改变时,通知所有的观察者。
  • 观察者Observer: 观察者也需要实现一个接口,包含一个更新方法,在接收主题通知时执行对应的操作。
  • 具体主题ConcreteSubject: 主题的具体实现, 维护一个观察者列表,包含了观察者的注册、删除和通知方法。
  • 具体观察者ConcreteObserver: 观察者接口的具体实现,每个具体观察者都注册到具体主题中,当主题状态变化并通知到具体观察者,具体观察者进行处理。

观察者模式的基本实现

根据上面的类图,我们可以写出观察者模式的基本实现

Java
// 主题接口 (主题)
interface Subject {
    // 注册观察者
    void registerObserver(Observer observer);
    // 移除观察者
    void removeObserver(Observer observer);
    // 通知观察者
    void notifyObservers();
}
// 观察者接口 (观察者)
interface Observer {
    // 更新方法
    void update(String message);
}

// 具体主题实现
class ConcreteSubject implements Subject {
    // 观察者列表
    private List<Observer> observers = new ArrayList<>();
    // 状态
    private String 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(state);
        }
    }
    // 更新状态
    public void setState(String state) {
        this.state = state;
        notifyObservers();
    }
}

// 具体观察者实现
class ConcreteObserver implements Observer {
    // 更新方法
    @Override
    public void update(String message) {
    }
}

什么时候使用观察者模式

观察者模式特别适用于 一个对象的状态变化会影响到其他对象,并且希望这些对象在状态变化时能够自动更新的情况。 比如说在图形用户界面中,按钮、滑动条等组件的状态变化可能需要通知其他组件更新,这使得观察者模式被广泛应用于GUI框架,比如Java的Swing框架。

此外,观察者模式在前端开发和分布式系统中也有应用。比较典型的例子是前端框架Vue,它使用响应式系统(Reactivity System)实现了观察者模式:当数据发生变化时,依赖该数据的视图会自动更新,这被称为"响应式更新"。在Vue中,每个组件实例都对应一个观察者,当数据发生变化时,观察者会收到通知并更新视图。

而在分布式系统中,观察者模式可以用于实现节点之间的消息通知机制。当一个节点的状态发生变化时,可以通知其他相关节点,实现分布式系统的协调和同步。例如,在微服务架构中,可以使用事件总线(Event Bus)实现服务之间的解耦通信,当一个服务的状态发生变化时,可以发布事件,其他订阅该事件的服务会收到通知并做出相应的处理。

相关实现

Java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

// 观察者接口
interface Observer {
    void update(int hour);
}

// 主题接口
interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

// 具体主题实现
class Clock implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private int hour = 0;

    @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(hour);
        }
    }

    public void tick() {
        hour = (hour + 1) % 24; // 模拟时间的推移
        notifyObservers();
    }
}

// 具体观察者实现
class Student implements Observer {
    private String name;

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

    @Override
    public void update(int hour) {
        System.out.println(name + " " + hour);
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 读取学生数量
        int N = scanner.nextInt();

        // 创建时钟
        Clock clock = new Clock();

        // 注册学生观察者
        for (int i = 0; i < N; i++) {
            String studentName = scanner.next();
            clock.registerObserver(new Student(studentName));
        }

        // 读取时钟更新次数
        int updates = scanner.nextInt();

        // 模拟时钟每隔一个小时更新一次
        for (int i = 0; i < updates; i++) {
            clock.tick();
        }
    }
}

其他语言版本

C++

C++
#include <iostream>
#include <vector>
#include <algorithm> 

// 观察者接口
class Observer {
public:
    virtual void update(int hour) = 0;
    virtual ~Observer() = default; // 添加虚析构函数
};

// 主题接口
class Subject {
public:
    virtual void registerObserver(Observer* observer) = 0;
    virtual void removeObserver(Observer* observer) = 0;
    virtual void notifyObservers() = 0;
    virtual ~Subject() = default; // 添加虚析构函数
};

// 具体主题实现
class Clock : public Subject {
private:
    std::vector<Observer*> observers;
    int hour;

public:
    Clock() : hour(0) {}

    void registerObserver(Observer* observer) override {
        observers.push_back(observer);
    }

    void removeObserver(Observer* observer) override {
        auto it = std::find(observers.begin(), observers.end(), observer);
        if (it != observers.end()) {
            observers.erase(it);
        }
    }

    void notifyObservers() override {
        for (Observer* observer : observers) {
            observer->update(hour);
        }
    }

    // 添加获取观察者的函数
    const std::vector<Observer*>& getObservers() const {
        return observers;
    }

    void tick() {
        hour = (hour + 1) % 24; // 模拟时间的推移
        notifyObservers();
    }
};

// 具体观察者实现
class Student : public Observer {
private:
    std::string name;

public:
    Student(const std::string& name) : name(name) {}

    void update(int hour) override {
        std::cout << name << " " << hour << std::endl;
    }
};

int main() {
    // 读取学生数量
    int N;
    std::cin >> N;

    // 创建时钟
    Clock clock;

    // 注册学生观察者
    for (int i = 0; i < N; i++) {
        std::string studentName;
        std::cin >> studentName;
        clock.registerObserver(new Student(studentName));
    }

    // 读取时钟更新次数
    int updates;
    std::cin >> updates;

    // 模拟时钟每隔一个小时更新一次
    for (int i = 0; i < updates; i++) {
        clock.tick();
    }

    // 释放动态分配的观察者对象
    for (Observer* observer : clock.getObservers()) {
        delete observer;
    }

    return 0;
}

Python

Python
from typing import List

# 观察者接口
class Observer:
    def update(self, hour: int):
        pass

# 主题接口
class Subject:
    def register_observer(self, observer: Observer):
        pass

    def remove_observer(self, observer: Observer):
        pass

    def notify_observers(self):
        pass

# 具体主题实现
class Clock(Subject):
    def __init__(self):
        self.observers: List[Observer] = []
        self.hour = 0

    def register_observer(self, observer: Observer):
        self.observers.append(observer)

    def remove_observer(self, observer: Observer):
        self.observers.remove(observer)

    def notify_observers(self):
        for observer in self.observers:
            observer.update(self.hour)

    def tick(self):
        self.hour = (self.hour + 1) % 24  # 模拟时间的推移
        self.notify_observers()

# 具体观察者实现
class Student(Observer):
    def __init__(self, name: str):
        self.name = name

    def update(self, hour: int):
        print(f"{self.name} {hour}")

if __name__ == "__main__":
    # 读取学生数量
    N = int(input())

    # 创建时钟
    clock = Clock()

    # 注册学生观察者
    for _ in range(N):
        student_name = input()
        clock.register_observer(Student(student_name))

    # 读取时钟更新次数
    updates = int(input())

    # 模拟时钟每隔一个小时更新一次
    for _ in range(updates):
        clock.tick()

Go

Go
package main

import (
    "fmt"
)

// 观察者接口
type Observer interface {
    Update(hour int)
}

// 主题接口
type Subject interface {
    RegisterObserver(observer Observer)
    RemoveObserver(observer Observer)
    NotifyObservers()
}

// 具体主题实现
type Clock struct {
    observers []Observer
    hour      int
}

func (c *Clock) RegisterObserver(observer Observer) {
    c.observers = append(c.observers, observer)
}

func (c *Clock) RemoveObserver(observer Observer) {
    for i, obs := range c.observers {
        if obs == observer {
            c.observers = append(c.observers[:i], c.observers[i+1:]...)
            break
        }
    }
}

func (c *Clock) NotifyObservers() {
    for _, observer := range c.observers {
        observer.Update(c.hour)
    }
}

func (c *Clock) Tick() {
    c.hour = (c.hour + 1) % 24 // 模拟时间的推移
    c.NotifyObservers()
}

// 具体观察者实现
type Student struct {
    name string
}

func NewStudent(name string) *Student {
    return &Student{name: name}
}

func (s *Student) Update(hour int) {
    fmt.Println(s.name, hour)
}

func main() {
    // 读取学生数量
    var N int
    fmt.Scan(&N)

    // 创建时钟
    clock := &Clock{}

    // 注册学生观察者
    for i := 0; i < N; i++ {
        var studentName string
        fmt.Scan(&studentName)
        clock.RegisterObserver(NewStudent(studentName))
    }

    // 读取时钟更新次数
    var updates int
    fmt.Scan(&updates)

    // 模拟时钟每隔一个小时更新一次
    for i := 0; i < updates; i++ {
        clock.Tick()
    }
}

TypeScript

TypeScript
interface ISubject {
  add(observer: IObserver): void;
  remove(observer: IObserver): void;
  notify(msg: string): void;
}

interface IObserver {
  update(msg: string): void;
}

class Clock implements ISubject {
  private observers: IObserver[]

  constructor() {
    this.observers = [];
  }

  add(observer: IObserver) {
    this.observers.push(observer);
  }

  remove(observer: IObserver) {
    this.observers = this.observers.filter((item) => item!== observer);
  }

  notify(msg: string) {
    this.observers.forEach((item) => {
      item.update(msg);
    });
  }
}

class Student implements IObserver {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  update(msg: string) {
    console.log(`${this.name} ${msg}`);
  }
}

// @ts-ignore
entry(2, (...args) => {
  return (time: number) => {
    const timeSubject = new Clock();
    args.forEach((item) => {
      timeSubject.add(new Student(item));
    });

    for (let i = 1; i <= time; i++) {
      timeSubject.notify(i.toString());
    }
  };
})("Alice")("Bob")(3);

function entry(count: number, fn: (...args: any) => void) {
  function dfs(...args) {
    if (args.length < count) {
      return (arg) => dfs(...args, arg);
    }
    return fn(...args);
  }
  return dfs;
}

评论