观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会收到通知并自动更新。它常用于事件驱动系统,如 GUI 框架、发布-订阅系统等。

观察者模式的组成

观察者模式通常包含以下几个角色:

  1. 主题(Subject):被观察的对象,维护一组观察者,提供添加、删除观察者的方法,并在状态变化时通知所有观察者。
  2. 观察者(Observer):接收主题状态变化通知的对象,通常定义一个更新方法(如 update())。
  3. 具体主题(Concrete Subject):主题的具体实现,存储状态并在状态变化时通知观察者。
  4. 具体观察者(Concrete Observer):观察者的具体实现,定义状态更新后的具体行为。

工作原理

  1. 观察者向主题注册,表明自己需要接收状态变化通知。
  2. 主题状态发生变化时,调用通知方法,遍历所有注册的观察者,调用它们的更新方法。
  3. 观察者收到通知后,根据主题的状态执行相应的逻辑。

UML 类图

┌────────────────┐       ┌────────────────┐
│    Subject     │       │    Observer    │
├────────────────┤       ├────────────────┤
│ observers      │<----->│ update()       │
│ attach()       │       └────────────────┘
│ detach()       │
│ notify()       │
└────────────────┘
       ↑
┌────────────────┐       ┌────────────────┐
│ConcreteSubject │       │ConcreteObserver│
├────────────────┤       ├────────────────┤
│ state          │       │ state          │
│ getState()     │       │ update()       │
└────────────────┘       └────────────────┘

代码示例(以 Java 为例)

以下是一个简单的观察者模式实现,模拟新闻发布系统:

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

// 观察者接口
interface Observer {
    void update(String message);
}

// 具体观察者:订阅者
class Subscriber implements Observer {
    private String name;

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

    @Override
    public void update(String message) {
        System.out.println(name + " 收到新闻: " + message);
    }
}

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

// 具体主题:新闻机构
class NewsAgency implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String news;

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

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

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

    public void setNews(String news) {
        this.news = news;
        System.out.println("新闻更新: " + news);
        notifyObservers();
    }
}

// 测试代码
public class Main {
    public static void main(String[] args) {
        NewsAgency agency = new NewsAgency();

        // 创建观察者
        Subscriber sub1 = new Subscriber("张三");
        Subscriber sub2 = new Subscriber("李四");

        // 注册观察者
        agency.attach(sub1);
        agency.attach(sub2);

        // 发布新闻
        agency.setNews("今天是晴天!");

        // 移除一个观察者
        agency.detach(sub1);

        // 发布新新闻
        agency.setNews("明天有雨!");
    }
}

输出:

新闻更新: 今天是晴天!
张三 收到新闻: 今天是晴天!
李四 收到新闻: 今天是晴天!
新闻更新: 明天有雨!
李四 收到新闻: 明天有雨!

观察者模式的特点

  • 优点
  • 支持一对多的依赖关系,主题和观察者松耦合。
  • 动态添加或移除观察者,灵活性高。
  • 符合“开闭原则”,易于扩展新观察者。
  • 缺点
  • 如果观察者数量过多,通知过程可能耗时。
  • 观察者与主题间可能存在循环依赖,导致系统复杂化。
  • 观察者需要自行处理更新逻辑,可能增加维护成本。

使用场景

  1. 当一个对象的状态变化需要通知其他对象,且这些对象是动态变化的:
  • 事件监听系统(如 GUI 按钮点击事件)。
  • 发布-订阅系统(如消息队列、新闻订阅)。
  • 实时数据监控(如股票价格更新)。
  1. 需要实现松耦合的事件处理机制。

注意事项

  • 通知机制:可以采用同步或异步通知,异步通知可提高性能但需注意线程安全。
  • 弱引用:为避免内存泄漏,可使用弱引用存储观察者。
  • 状态传递:通知时可以传递主题的完整状态或仅传递变化的部分,视需求而定。
  • 与发布-订阅模式的区别:观察者模式中主题和观察者直接关联,而发布-订阅模式通常通过中间件(如消息代理)解耦。

总结

观察者模式通过建立主题与观察者间的松耦合关系,实现了状态变化的动态通知,广泛应用于事件驱动的场景。它在 GUI 框架、消息系统和实时监控中非常实用,但在设计时需注意通知效率和依赖管理,以避免性能或复杂度问题。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注