美文网首页
设计模式之观察者模式 2022-03-10

设计模式之观察者模式 2022-03-10

作者: 9_SooHyun | 来源:发表于2022-03-10 17:22 被阅读0次

观察者模式做什么

观察者模式实现了对象之间的多对1依赖,一旦新数据发布所有订阅者都将收到通知并自动更新

观察者模式核心——interface

观察者模式-核心就是interface接口

为什么要设计接口?看个案例
现在有一个主题发布者Subject s,被一个订阅者Observer1 o1订阅。那么伪代码如下:

class Subject:
    string topic
    string content
    string getTopic() { return topic}
    string getContent() {return content }
    void notifyAll() {
        string t = getTopic()
        string c = getContent()
        // 调用o1的updateMyself方法更新o1
        o1.updateMyself(t, c)
    }
    
class Observer1:
    void updateMyself(string topic, content) { ... }

某一天来了另外一个订阅者Observer2 o2,新增代码后变成:

class Subject:
    string topic
    string content
    string getTopic() { return topic}
    string getContent() {return content }
    void notifyAll() {
        string t = getTopic()
        string c = getContent()
        // 调用o1的updateMyself方法更新o1
        o1.updateMyself(t, c)
        // 调用o2的updateMe方法更新o2
        o2.updateMe(t, c)
    }
    
class Observer1:
    void updateMyself(string topic, content) { ... }

class Observer2:
    void updateMe(string topic, content) { ... }

由于Observer1和Observer2的更新方法不同,每来一个订阅者,Subject就不得不去适配该订阅者的更新方法:

  • 假设不断地有新类型的订阅者过来,那么就要不断地往Subject.notifyAll里面加代码
  • 假设不断地有订阅者退出,那么就要不断地往Subject.notifyAll里面删代码
  • 假设不断地有订阅者修改了它们的update方法,那么就要不断地往Subject.notifyAll里面改代码
  • 不支持运行时订阅者的增减

这就是面向每个订阅者的update()实现的编程,非常死板

没有接口,就没有协议。各玩各的一套,只能是疲于适配。因此,观察者需统一实现update接口
只要所有观察者将更新方法都统一一个函数签名,那对于Subject就非常友好了。Subject无需再关注每个订阅者具体的更新方法,因为订阅者的更新方法拥有相同的函数签名。代码如下:

class Subject:
    string topic
    string content
    string getTopic() { return topic}
    string getContent() {return content }
    void notifyAll() {
        string t = getTopic()
        string c = getContent()
        // 调用o1的update方法更新o1
        o1.update(t, c)
        // 调用o2的update方法更新o2
        o2.update(t, c)
    }

// 观察者interface
interface Observer {
    void update(string topic, content)
}

class Observer1 implements Observer:
    void update(string topic, content) { ... }

class Observer2 implements Observer:
    void update(string topic, content) { ... }

但是,现在实现/取消订阅,还是通过在Subject.notifyAll()里面增加/删除代码来实现,扩展性很差
于是,Subject还需要一个注册订阅者的方法,需要一个删除订阅者的方法:

class Subject:
    string topic
    string content
    list observers

    string getTopic() { return topic}
    string getContent() {return content }

    void notifyAll() {
        string t = getTopic()
        string c = getContent()
        for each o in observers {
            o.update(t, c)
        }
    }

    void addObserver(Observer o) {
        observers.add(o)
    }

    void removeObserver(Observer o) {
        observers.remove(o)
    }

// 观察者interface
interface Observer {
    void update(string topic, content)
}

class Observer1 implements Observer:
    void update(string topic, content) { ... }

class Observer2 implements Observer:
    void update(string topic, content) { ... }

好了,这个时候,如果这些订阅者还想去订阅其他subject呢?自然的思路是,其他subject的类也实现void addObserver(Observer o)void removeObserver(Observer o)void notifyAll()——这又回到接口上来了。主题类也可以抽象成接口以便于扩展

// 主题interface
interface Subject{
    void notifyAll()
    void addObserver(Observer o) 
    void removeObserver(Observer o)
}

// 观察者interface
interface Observer {
    void update(string topic, content)
}

class ConcreteSubject implements Subject:
    string topic
    string content
    list observers

    string getTopic() { return topic}
    string getContent() {return content }

    void notifyAll() {
        string t = getTopic()
        string c = getContent()
        for each o in observers {
            o.update(t, c)
        }
    }

    void addObserver(Observer o) {
        observers.add(o)
    }

    void removeObserver(Observer o) {
        observers.remove(o)
    }


class Observer1 implements Observer:
    void update(string topic, content) { ... }

class Observer2 implements Observer:
    void update(string topic, content) { ... }

还有一个问题,上面的所有例子中,订阅者需要被通知什么信息,是通过Observer.update()的参数约定的,例子中通知的数据是topic和content
如果部分订阅者只需要topic,部分只需要content,部分还需要额外的数据呢?那void update(string topic, content)就不满足需求了
如何既保持统一的接口,又拥有各自的更新逻辑呢?——把整个"数据源"作为Observer.update()的参数

// 主题interface
interface Subject{
    void notifyAll()
    void addObserver(Observer o) 
    void removeObserver(Observer o)
    string getTopic()
    string getContent()
}

// 观察者interface
interface Observer {
    // 把整个"数据源"拉过来,把整个subject拿过来,想要什么数据自己内部取
    void update(Subject s)
}

class ConcreteSubject implements Subject:
    string topic
    string content
    list observers

    string getTopic() { return topic}
    string getContent() {return content }

    void notifyAll() {
        string t = getTopic()
        string c = getContent()
        for each o in observers {
            o.update(this)
        }
    }

    void addObserver(Observer o) {
        observers.add(o)
    }

    void removeObserver(Observer o) {
        observers.remove(o)
    }


class Observer1 implements Observer:
    void update(Subject s) { 
        string t = s.getTopic()
        // update t ...
    }

class Observer2 implements Observer:
    void update(Subject s) { 
        string c = s.getContent()
        // update c ...
    }

小结

  • 协商好接口(interface),所有实例都统一按照interface规定的标准去交互,避免了不同对象的method之间的差异,便于扩展
    不同的对象按照interface去做各自的内部实现,它们将拥有相同的函数签名(method signature),内部却是不同的函数体(method bodies, aka implementation)——暴露出去的是公共的、统一的,内部逻辑是私有的、独占的

  • notify无外乎两种方式:推和拉
    推:需要事先约定好observer需要的数据,subject直接推送这些数据
    拉:当不同的observer需要的数据在类型和数量上存在差异时,可以通过observer拉的方式:observer调用subject暴露的方法拉取自己所需的数据

相关文章

网友评论

      本文标题:设计模式之观察者模式 2022-03-10

      本文链接:https://www.haomeiwen.com/subject/xqxqdrtx.html