观察者模式

35 阅读3分钟

观察者模式是一种行为设计模式,允许你定义一种订阅机制,可在对象事件发生时通知多个“观察”该对象的其他对象。

发布类包括(1)一个用于存储订阅对象引用的列表成员变量; (2)几个用于添加或删除该列表中订阅者的公有方法。

341352431-3aeb9070-794f-4695-87e6-fad5ce06db0b.png

实际应用中可能会有十几个不同的订阅者类跟踪着同一个发布者类的事件,为了降低发布类和订阅类的耦合,所有订阅者必须实现同样的接口,发布者仅通过该接口与订阅者交互。接口中必须声明通知方法以及参数,这样发布者在发出通知时还能传递一些上下文数据。

341355304-9b80bc27-66ba-4551-8ea5-611529ac4431.png

如果你的应用中有多个不同类型的发布者,且希望订阅者可兼容所有发布者,那么你甚至可以进一步让所有发布者遵循同样的接口。

观察者模式结构

341358313-97046721-f648-4a15-9a0d-87251d55be15.png

伪代码

341359589-c6c514de-17c7-426c-b3eb-90f0bfec5f7c.png

订阅者列表是动态生成的:对象可在运行时根据程序需要开始或停止监听通知, 在本实现中,编辑器类自身并不维护订阅列表。它将工作委派给专门从事此工作的一个特殊帮手对象。你还可将该对象升级为中心化的事件分发器,允许任何对象成为发布者。 只要发布者通过同样的接口与所有订阅者进行交互,那么在程序中新增订阅者时就无需修改已有发布类的代码。

// 发布者基类包含订阅管理代码和通知方法。
class EventManager is
    private field listeners: hash map of event types and listeners

    method subscribe(eventType, listener) is
        listeners.add(eventType, listener)

    method unsubscribe(eventType, listener) is
        listeners.remove(eventType, listener)

    method notify(eventType, data) is
        foreach (listener in listeners.of(eventType)) do
            listener.update(data)

// 具体发布者包含一些订阅者感兴趣的实际业务逻辑。我们可以从发布者基类中扩
// 展出该类,但在实际情况下并不总能做到,因为具体发布者可能已经是子类了。
// 在这种情况下,你可用组合来修补订阅逻辑,就像我们在这里做的一样。
class Editor is
    public field events: EventManager
    private field file: File

    constructor Editor() is
        events = new EventManager()

    // 业务逻辑的方法可将变化通知给订阅者。
    method openFile(path) is
        this.file = new File(path)
        events.notify("open", file.name)

    method saveFile() is
        file.write()
        events.notify("save", file.name)

    // ……


// 这里是订阅者接口。如果你的编程语言支持函数类型,则可用一组函数来代替整
// 个订阅者的层次结构。
interface EventListener is
    method update(filename)

// 具体订阅者会对其注册的发布者所发出的更新消息做出响应。
class LoggingListener implements EventListener is
    private field log: File
    private field message: string

    constructor LoggingListener(log_filename, message) is
        this.log = new File(log_filename)
        this.message = message

    method update(filename) is
        log.write(replace('%s',filename,message))

class EmailAlertsListener implements EventListener is
    private field email: string
    private field message: string

    constructor EmailAlertsListener(email, message) is
        this.email = email
        this.message = message

    method update(filename) is
        system.email(email, replace('%s',filename,message))


// 应用程序可在运行时配置发布者和订阅者。
class Application is
    method config() is
        editor = new Editor()

        logger = new LoggingListener(
            "/path/to/log.txt",
            "有人打开了文件:%s");
        editor.events.subscribe("open", logger)

        emailAlerts = new EmailAlertsListener(
            "admin@example.com",
            "有人更改了文件:%s")
        editor.events.subscribe("save", emailAlerts)