观察者模式

概念

观察者模式定义了一种'一对多'的得依赖关系, 让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时, 会通知所有观察者对象, 使它们能够根据根据变化做一些事情。

具体实现:

  1. 定义一个主题接口, 接口方法有:添加主题, 删除主题, 通知函数
  2. 定义一个观察者接口, 接口方法:OnNotify 被通知
  3. 实现主题接口, 主题接口保存一个观察者列表
  4. 实现不同的观察者, 观察者可以注册到主题

观察者模式和发布订阅模式的区别:

  • 观察者模式有一个主题对象, 一个观察者对象。主题变化,主动通知观察者。
  • 发布订阅模式:有3个对象, 发布者, 订阅者, 调度中心。 发布者发布一个主题消息, 调度中心将消息转发给订阅了这个主题的订阅者。

使用观察者模式的场景和优缺点

使用场景

关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
事件多级触发场景。
跨系统的消息交换场景,如消息队列、事件总线的处理机制。

优点

  • 解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。

缺点

  • 在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,如果一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

代码实现


package main

import (
    "fmt"
)

// Event 消息 1:吃饭
type Event struct {
    Data int64
}

// 观察者接口
type Observer interface {
    // 消息变动通知观察者
    OnNotify(Event)
}

// 主题
type Subject interface {

    // 添加主题
    Attach(Observer)
    // 删除主题
    DeAttach(Observer)
    // 通知消息
    Notify(Event)
}

// 主题具体实现
type SubjectImpl struct {
    // 用map来存放Observer
    observers map[Observer]struct{}
}

// Attach ...
// 传入的l变量(ConcreteObserver1结构体变量)无论是指针还是值类型, 根据实现Observer结构体的接收器类型有关, 当然无论接收器类型是指针类型,值类型无所谓, map 匹配结构体本质上, 是判断结构体变量是否相等, 并不关心是否是源数据还是copy的数据.
func (o *SubjectImpl) Attach(l Observer) {
    o.observers[l] = struct{}{}
}

// DeAttach ...
func (o *SubjectImpl) DeAttach(l Observer) {
    delete(o.observers, l)
}

// Notify 把变化的消息发送给所有的观察者
func (o *SubjectImpl) Notify(e Event) {
    for i := range o.observers {
        i.OnNotify(e)
    }
}

// 具体观察者1
type ConcreteObserver1 struct {
}

// OnNotify 收到消息变动
func (o *ConcreteObserver1) OnNotify(e Event) {
    if e.Data == 1 {
        fmt.Println("我是1号, 我要吃面包")
    }
}

// 具体观察者2
type ConcreteObserver2 struct {
}

// OnNotify 收到消息变动
func (o *ConcreteObserver2) OnNotify(e Event) {
    if e.Data == 1 {
        fmt.Println("我是2号, 我要吃面条")
    }
}

func main() {
    // 初始化
    n := SubjectImpl{
        observers: map[Observer]struct{}{},
    }

    // 注册两个观察者
    n.Attach(&ConcreteObserver1{})
    n.Attach(&ConcreteObserver2{})

    // 消息通知
    n.Notify(Event{Data: 1})
}


订阅-发布模式

在现在的发布订阅模式中,称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为调度中心或事件通道,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。

举一个例子,你在微博上关注了A,同时其他很多人也关注了A,那么当A发布动态的时候,微博就会为你们推送这条动态。A就是发布者,你是订阅者,微博就是调度中心,你和A是没有直接的消息往来的,全是通过微博来协调的(你的关注,A的发布动态)。

订阅发布模式和观察者模式的区别

  1. 订阅发布模式, 发布订阅模式相比观察者模式多了个事件通道,事件通道作为调度中心,管理事件的订阅和发布工作,彻底隔绝了订阅者和发布者的依赖关系。即订阅者在订阅事件的时候,只关注事件本身,而不关心谁会发布这个事件;发布者在发布事件的时候,只关注事件本身,而不关心谁订阅了这个事件。
  2. 观察者模式有两个重要的角色,即目标和观察者。在目标和观察者之间是没有事件通道的。一方面,观察者要想订阅目标事件,由于没有事件通道,因此必须将自己添加到目标(Subject) 中进行管理;另一方面,目标在触发事件的时候,也无法将通知操作(notify) 委托给事件通道,因此只能亲自去通知所有的观察者。

概念

观察者模式定义了一种'一对多'的得依赖关系, 让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时, 会通知所有观察者对象, 使它们能够根据根据变化做一些事情。

具体实现:

  1. 定义一个主题接口, 接口方法有:添加主题, 删除主题, 通知函数
  2. 定义一个观察者接口, 接口方法:OnNotify 被通知
  3. 实现主题接口, 主题接口保存一个观察者列表
  4. 实现不同的观察者, 观察者可以注册到主题

观察者模式和发布订阅模式的区别:

  • 观察者模式有一个主题对象, 一个观察者对象。主题变化,主动通知观察者。
  • 发布订阅模式:有3个对象, 发布者, 订阅者, 调度中心。 发布者发布一个主题消息, 调度中心将消息转发给订阅了这个主题的订阅者。

使用观察者模式的场景和优缺点

使用场景

关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
事件多级触发场景。
跨系统的消息交换场景,如消息队列、事件总线的处理机制。

优点

  • 解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。

缺点

  • 在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,如果一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

代码实现


package main

import (
    "fmt"
)

// Event 消息 1:吃饭
type Event struct {
    Data int64
}

// 观察者接口
type Observer interface {
    // 消息变动通知观察者
    OnNotify(Event)
}

// 主题
type Subject interface {

    // 添加主题
    Attach(Observer)
    // 删除主题
    DeAttach(Observer)
    // 通知消息
    Notify(Event)
}

// 主题具体实现
type SubjectImpl struct {
    // 用map来存放Observer
    observers map[Observer]struct{}
}

// Attach ...
// 传入的l变量(ConcreteObserver1结构体变量)无论是指针还是值类型, 根据实现Observer结构体的接收器类型有关, 当然无论接收器类型是指针类型,值类型无所谓, map 匹配结构体本质上, 是判断结构体变量是否相等, 并不关心是否是源数据还是copy的数据.
func (o *SubjectImpl) Attach(l Observer) {
    o.observers[l] = struct{}{}
}

// DeAttach ...
func (o *SubjectImpl) DeAttach(l Observer) {
    delete(o.observers, l)
}

// Notify 把变化的消息发送给所有的观察者
func (o *SubjectImpl) Notify(e Event) {
    for i := range o.observers {
        i.OnNotify(e)
    }
}

// 具体观察者1
type ConcreteObserver1 struct {
}

// OnNotify 收到消息变动
func (o *ConcreteObserver1) OnNotify(e Event) {
    if e.Data == 1 {
        fmt.Println("我是1号, 我要吃面包")
    }
}

// 具体观察者2
type ConcreteObserver2 struct {
}

// OnNotify 收到消息变动
func (o *ConcreteObserver2) OnNotify(e Event) {
    if e.Data == 1 {
        fmt.Println("我是2号, 我要吃面条")
    }
}

func main() {
    // 初始化
    n := SubjectImpl{
        observers: map[Observer]struct{}{},
    }

    // 注册两个观察者
    n.Attach(&ConcreteObserver1{})
    n.Attach(&ConcreteObserver2{})

    // 消息通知
    n.Notify(Event{Data: 1})
}


订阅-发布模式

在现在的发布订阅模式中,称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为调度中心或事件通道,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。

举一个例子,你在微博上关注了A,同时其他很多人也关注了A,那么当A发布动态的时候,微博就会为你们推送这条动态。A就是发布者,你是订阅者,微博就是调度中心,你和A是没有直接的消息往来的,全是通过微博来协调的(你的关注,A的发布动态)。

订阅发布模式和观察者模式的区别

  1. 订阅发布模式, 发布订阅模式相比观察者模式多了个事件通道,事件通道作为调度中心,管理事件的订阅和发布工作,彻底隔绝了订阅者和发布者的依赖关系。即订阅者在订阅事件的时候,只关注事件本身,而不关心谁会发布这个事件;发布者在发布事件的时候,只关注事件本身,而不关心谁订阅了这个事件。
  2. 观察者模式有两个重要的角色,即目标和观察者。在目标和观察者之间是没有事件通道的。一方面,观察者要想订阅目标事件,由于没有事件通道,因此必须将自己添加到目标(Subject) 中进行管理;另一方面,目标在触发事件的时候,也无法将通知操作(notify) 委托给事件通道,因此只能亲自去通知所有的观察者。

代码实现

package main

import "fmt"

type Event struct {
    Message string
}

type ControlCenter interface {
    Subscribe(topic string, f func(e *Event))
    Unsubscribe()
    Publish()
}

// 每个订阅者对应一个callback
type Subscribers map[string]func(e *Event)

type ControlCenterIml struct {
    // 一个主题对应多个订阅者
    TopicEvent map[string]Subscribers
}

func (c *ControlCenterIml) Subscribe(subcriberId, topic string, f func(e *Event)) {
    if c.TopicEvent == nil {
        return
    }
    if v, ok := c.TopicEvent[topic]; !ok {
        subscribers := make(Subscribers)
        subscribers[subcriberId] = f
        c.TopicEvent[topic] = subscribers
    } else {
        v[subcriberId] = f
        c.TopicEvent[topic] = v
    }
}

func (c *ControlCenterIml) Unsubscribe(subcriberId, topic string) {
    if c.TopicEvent == nil {
        return
    }
    if subscribers, ok := c.TopicEvent[topic]; !ok {
        return
    } else {
        delete(subscribers, subcriberId)
    }
}

func (c *ControlCenterIml) Publish(topic string, e *Event) bool {
    if c.TopicEvent == nil {
        return false
    }
    var subscribers Subscribers
    if v, ok := c.TopicEvent[topic]; !ok {
        return false
    } else {
        subscribers = v
    }

    for k, v := range subscribers {
        fmt.Println(k)
        v(e)
    }
    return true
}

func main() {
    subPub := ControlCenterIml{TopicEvent: make(map[string]Subscribers)}
    subPub.Subscribe("123", "study", func(e *Event) {
        fmt.Println("recv something:", e.Message)
    })
    subPub.Subscribe("124", "study", func(e *Event) {
        fmt.Println("recv something:", e.Message)
    })
    subPub.Publish("study", &Event{Message: "hello lili"})
    subPub.Unsubscribe("123", "study")
    subPub.Publish("study", &Event{Message: "hello vivi"})
}

代码实现

package main

import "fmt"

type Event struct {
    Message string
}

type ControlCenter interface {
    Subscribe(topic string, f func(e *Event))
    Unsubscribe()
    Publish()
}

// 每个订阅者对应一个callback
type Subscribers map[string]func(e *Event)

type ControlCenterIml struct {
    // 一个主题对应多个订阅者
    TopicEvent map[string]Subscribers
}

func (c *ControlCenterIml) Subscribe(subcriberId, topic string, f func(e *Event)) {
    if c.TopicEvent == nil {
        return
    }
    if v, ok := c.TopicEvent[topic]; !ok {
        subscribers := make(Subscribers)
        subscribers[subcriberId] = f
        c.TopicEvent[topic] = subscribers
    } else {
        v[subcriberId] = f
        c.TopicEvent[topic] = v
    }
}

func (c *ControlCenterIml) Unsubscribe(subcriberId, topic string) {
    if c.TopicEvent == nil {
        return
    }
    if subscribers, ok := c.TopicEvent[topic]; !ok {
        return
    } else {
        delete(subscribers, subcriberId)
    }
}

func (c *ControlCenterIml) Publish(topic string, e *Event) bool {
    if c.TopicEvent == nil {
        return false
    }
    var subscribers Subscribers
    if v, ok := c.TopicEvent[topic]; !ok {
        return false
    } else {
        subscribers = v
    }

    for k, v := range subscribers {
        fmt.Println(k)
        v(e)
    }
    return true
}

func main() {
    subPub := ControlCenterIml{TopicEvent: make(map[string]Subscribers)}
    subPub.Subscribe("123", "study", func(e *Event) {
        fmt.Println("recv something:", e.Message)
    })
    subPub.Subscribe("124", "study", func(e *Event) {
        fmt.Println("recv something:", e.Message)
    })
    subPub.Publish("study", &Event{Message: "hello lili"})
    subPub.Unsubscribe("123", "study")
    subPub.Publish("study", &Event{Message: "hello vivi"})
}

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容