【问题标题】:The Go way to implement the Observer design pattern实现观察者设计模式的 Go 方式
【发布时间】:2019-07-14 11:47:37
【问题描述】:

我的经验是使用 OOP 语言,但我已经开始尝试 Go。我无法找出在 Go 中实现 Observer design pattern 的最佳方法。

我按如下方式组织了我的项目,其中observers 文件夹中的所有内容都是package observers 的一部分,subjects 文件夹中的所有内容都是package subjects 的一部分。在main.go 中完成了观察者对主题的附加。

my-project/
  main.go
  observers/
    observer.go
    observer_one.go
    observer_two.go
  subjects/
    subject.go
    subject_one.go

我看过this section in the Go wiki关于接口:

Go 接口通常属于使用接口类型值的包,而不是实现这些值的包。实现包应该返回具体(通常是指针或结构)类型:这样,新方法可以添加到实现中,而无需大量重构。

牢记来自 Go Wiki 的评论。我已经这样实现了(省略了函数实现):

subject.go:

type ObserverInterface interface {
    Update(subject *Subject, updateType string)
}

type Subject struct {
    observers map[string][]ObserverInterface
}

func (s *Subject) Attach(observer ObserverInterface, updateType string) {}

func (s *Subject) Detach(observer ObserverInterface, updateType string) {}

func (s *Subject) notify(updateType string) {}

observer.go:

type SubjectInterface interface {
   Attach(observer Observer, updateType string)
   Detach(observer Observer, updateType string)
   notify(updateType string)
}

type Observer struct {
    uuid uuid.UUID
}

observer_one.go

type ObserverOne struct {
    Observer
}

func (o *ObserverOne) Update(subject *SubjectInterface, updateType string) {}

main.go

subjectOne := &SubjectOne{}
observerOne := &ObserverOne{Observer{uuid: uuid.New()}}
subjectOne.Attach(observerOne, "update_type")

我希望能够使用SubjectInterface 作为ObserverOneUpdate() 方法的参数,这样我就可以避免subject 包和observer 包之间存在依赖关系,但我得到以下信息编译时错误。

observers/observer_one.go:29:35: cannot use &observer (type *ObserverOne) as type subjects.ObserverInterface in argument to SubjectOne.Subject.Attach:
    *ObserverOne does not implement subjects.ObserverInterface (wrong type for Update method)
        have Update(*SubjectInterface, string)
        want Update(*subjects.Subject, string)

如果我将observer_one.go 中Update() 的定义替换为以下内容,它编译得很好,但我认为这个想法是使用接口解耦包:

func (o *ObserverOne) Update(subject *subjects.Subject, updateType string) {}

【问题讨论】:

标签: go design-patterns observer-pattern


【解决方案1】:

首先,不要使用指向接口的指针。

func (o *ObserverOne) Update(subject *SubjectInterface, updateType string) {}

应该是

func (o *ObserverOne) Update(subject SubjectInterface, updateType string) {}

其次,您已将接口定义为需要具体类型:

type ObserverInterface interface {
    Update(subject *Subject, updateType string)
}

相反,让它接受一个接口:

type ObserverInterface interface {
    Update(subject SubjectInterface, updateType string)
}

【讨论】:

  • 最后一点,目前SubjectInterface 在我的observers 包中的observer.go 中定义,所以有几种方法可以使ObserverInterface 中的Update 接受接口。 1.在我的subjects 包以及我的observers 包中定义SubjectInterface,2. 使用observers.SubjectInterface 或3. 在subjects 包中定义它,并根据需要在我的observers 中使用subjects.SubjectInterface包裹。 1. 似乎不对,因为在两个地方定义了相同的东西,并且 1. 和 3. 似乎与 Go 中推荐的做事方式背道而驰。
  • @thoward:你应该在一个独立于你的观察者和主题的包中定义你的接口。
【解决方案2】:

Flimzy 解决方案仍然将事物与显式接口绑定到 Observers 垂直抽象中(即:ObserverInterface)。

更通用的版本可能如下所示:

package main

import (
    "fmt"
)

func main() {

    obs := Observer{SubjectObserver{}}

    s := Subject{Observer: obs}
    s.Update()

}

type Subject struct {
    Observer
    UUID string
}

func (s *Subject) Update() {
    s.Observer.Notify(s)
}

type SubjectObserver struct{}

func (s SubjectObserver) Notify(v interface{}) {
    x, ok := v.(*Subject)
    if ok {
        fmt.Printf("do whatever with %#v\n", x)
    }
}

type Observer []ObserverOf

func (o Observer) Notify(s interface{}) {
    for _, ob := range o {
        ob.Notify(s)
    }
}

type ObserverOf interface {
    Notify(interface{})
}

PO 嵌入观察者是一个细节。

【讨论】:

  • 尽可能避免使用interface{}
  • “Flimzy 解决方案仍然将事物与显式接口联系在一起”我不明白您为什么认为这是一个问题。
  • 我认为你是一个困惑的人。 interface{} says nothing。仅在绝对必要时使用。
  • “在此处使用接口{} 可以简化很多事情”——不,在这里使用 interface{} 会使事情变得复杂很多,无论是现在还是未来。通过阅读函数签名,您对传递给该函数的内容一无所知。您必须查看实现。完全不理想。而且您获得零编译时类型安全性,这是首先使用静态类型语言的主要原因。
  • “我不记得一个函数签名仅仅通过它的参数定义就完全反映了它的副作用。”首先,函数应该努力不产生副作用。其次,那时你不是很努力。以下是一些示例:Open(path string) (*os.File, error)Read(p []byte) (count int, err error), Copy(dst Writer, src Reader) (written int64, err error).
猜你喜欢
  • 2010-12-24
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 2011-10-20
  • 1970-01-01
  • 1970-01-01
  • 2015-05-08
  • 1970-01-01
相关资源
最近更新 更多