Go语言中的观察者模式

huangapple go评论95阅读模式
英文:

Observer pattern in Go language

问题

这个问题非常常见:当某个事件发生时,一个对象应该通知所有的订阅者。在C++中,我们可以使用boost::signals或其他方式来实现。但是在Go语言中该如何实现呢?能否提供一些工作代码示例,其中有一对对象订阅了一个发布者并处理通知。

谢谢

英文:

This problem is pretty common: an object should notify all its subscribers when some event occurs. In C++ we may use boost::signals or something else. But how to do this in Go language? It would be nice to see some working code example where a couple of objects are subscribed to a publisher and process notifications.

Thanks

答案1

得分: 22

这在Go语言中实际上非常简单。使用通道(channels)即可。这就是它们的用途。

type Publish struct {
    listeners []chan *Msg
}

type Subscriber struct {
    Channel chan *Msg
}

func (p *Publisher) Sub(c chan *Msg) {
    p.appendListener(c)
}

func (p *Publisher) Pub(m *Msg) {
    for _, c := range p.listeners {
        c <- Msg
    }
}

func (s *Subscriber) ListenOnChannel() {
    for {
        data := <-s.Channel
        //处理数据
    }
}

func main() {
    for _, v := range subscribers {
        p.Sub(v.Channel)
        go v.ListenOnChannel()
    }
    //在这里进行某种等待
}

显然,这并不是一个完整的可工作的代码示例。但它很接近。

英文:

This is actually pretty simple in Go. Use channels. This is the kind of thing they're made for.

type Publish struct {
    listeners []chan *Msg
}

type Subscriber struct {
    Channel chan *Msg
}

func (p *Publisher) Sub(c chan *Msg) {
    p.appendListener(c)
}

func (p *Publisher) Pub(m *Msg) {
    for _, c := range p.listeners {
        c &lt;- Msg
    }
}

func (s *Subscriber) ListenOnChannel() {
    for {
        data := &lt;-s.Channel
        //Process data
    }
}

func main() {
    for _, v := range subscribers {
        p.Sub(v.Channel)
        go v.ListenOnChannel()
    }
    //Some kind of wait here
}

Obviously this isn't exactly a working code sample. But it's close.

答案2

得分: 2

这里我提供了一个没有通道的分类实现,可以自由参考这篇文章

假设的例子:
假设你对股票市场感兴趣。你有以下需求:你想追踪特定公司(例如苹果公司)的股票价格。你不想错过任何股票价格的更新,尤其是当价格下降到一定点时。你希望收到所有股票价格的更新通知。

接口:

// 主题接口
type Subject interface {
	Attach(o Observer) (bool, error)
	Detach(o Observer) (bool, error)
	Notify() (bool, error)
}

// 观察者接口
type Observer interface {
	Update(string)
}

具体观察者对象:

// 具体观察者:StockObserver
type StockObserver struct {
	name string
}

func (s *StockObserver) Update(t string) {
	// 做一些操作
	println("StockObserver:", s.name, "已更新,收到主题字符串:", t)
}

具体主题对象:

// 具体主题:stockMonitor
type StockMonitor struct {
	// 内部状态
	ticker string
	price  float64

	observers []Observer
}

func (s *StockMonitor) Attach(o Observer) (bool, error) {

	for _, observer := range s.observers {
		if observer == o {
			return false, errors.New("观察者已存在")
		}
	}
	s.observers = append(s.observers, o)
	return true, nil
}

func (s *StockMonitor) Detach(o Observer) (bool, error) {

	for i, observer := range s.observers {
		if observer == o {
			s.observers = append(s.observers[:i], s.observers[i+1:]...)
			return true, nil
		}
	}
	return false, errors.New("未找到观察者")
}

func (s *StockMonitor) Notify() (bool, error) {
	for _, observer := range s.observers {
		observer.Update(s.String())
	}
	return true, nil
}

func (s *StockMonitor) SetPrice(price float64) {
	s.price = price
	s.Notify()
}

func (s *StockMonitor) String() string {
	convertFloatToString := strconv.FormatFloat(s.price, 'f', 2, 64)
	return "StockMonitor: " + s.ticker + " $" + convertFloatToString
}

main.go


func main() {

	// 创建一个新的stockMonitor对象
	stockMonitor := &StockMonitor{
		ticker: "AAPL",
		price:  0.0,
	}

	observerA := &StockObserver{
		name: "观察者A",
	}
	observerB := &StockObserver{
		name: "观察者B",
	}

	// 将观察者附加到stockMonitor
	stockMonitor.Attach(observerA)
	stockMonitor.Attach(observerB)

	// 启动stockMonitor
	stockMonitor.Notify()

	// 更改stockMonitor的价格
	stockMonitor.SetPrice(500)

	// 从stockMonitor中分离一个观察者
	stockMonitor.Detach(observerA)

	// 更改stockMonitor的价格
	stockMonitor.SetPrice(528)
}

在这部分中:

  • 我们创建了两个观察者,observerA和observerB,并将它们附加到stockMonitor上。

  • 更改stockMonitor的价格。

  • 我们可以看到observerA和observerB都收到了通知。

  • 从stockMonitor中分离observerA,并更改股票价格。我们可以看到只有observerB收到了通知。

英文:

Here I give a classific implementation without channels, be free to refer this post
Go语言中的观察者模式

Assumed Example:
Suppose you are interested in the stock market. You have the following needs: You want to keep track of the stock prices of a particular company (e.g. Apple Inc). You would not like to miss any stock price update especially if the price is dropping to a certain point. You would like to be notified of all the stock price updates.

interfaces:

// Subject interface
type Subject interface {
Attach(o Observer) (bool, error)
Detach(o Observer) (bool, error)
Notify() (bool, error)
}
// Observer Interface
type Observer interface {
Update(string)
}

Concrete Observer object

// Concrete Observer: StockObserver
type StockObserver struct {
name string
}
func (s *StockObserver) Update(t string) {
// do something
println(&quot;StockObserver:&quot;, s.name, &quot;has been updated,&quot;, &quot;received subject string:&quot;, t)
}

Concrete Subject object

// Concrete Subject: stockMonitor
type StockMonitor struct {
// internal state
ticker string
price  float64
observers []Observer
}
func (s *StockMonitor) Attach(o Observer) (bool, error) {
for _, observer := range s.observers {
if observer == o {
return false, errors.New(&quot;Observer already exists&quot;)
}
}
s.observers = append(s.observers, o)
return true, nil
}
func (s *StockMonitor) Detach(o Observer) (bool, error) {
for i, observer := range s.observers {
if observer == o {
s.observers = append(s.observers[:i], s.observers[i+1:]...)
return true, nil
}
}
return false, errors.New(&quot;Observer not found&quot;)
}
func (s *StockMonitor) Notify() (bool, error) {
for _, observer := range s.observers {
observer.Update(s.String())
}
return true, nil
}
func (s *StockMonitor) SetPrice(price float64) {
s.price = price
s.Notify()
}
func (s *StockMonitor) String() string {
convertFloatToString := strconv.FormatFloat(s.price, &#39;f&#39;, 2, 64)
return &quot;StockMonitor: &quot; + s.ticker + &quot; $&quot; + convertFloatToString
}

main.go


func main() {
// Create a new stockMonitor object
stockMonitor := &amp;StockMonitor{
ticker: &quot;AAPL&quot;,
price:  0.0,
}
observerA := &amp;StockObserver{
name: &quot;Observer A&quot;,
}
observerB := &amp;StockObserver{
name: &quot;Observer B&quot;,
}
// Attach our Observers to the stockMonitor
stockMonitor.Attach(observerA)
stockMonitor.Attach(observerB)
// Start the stockMonitor
stockMonitor.Notify()
// Change the price of the stockMonitor
stockMonitor.SetPrice(500)
// Detach an Observer from the stockMonitor
stockMonitor.Detach(observerA)
// Change the price of the stockMonitor
stockMonitor.SetPrice(528)
}

In this part

  • We create two observers, observerA and observerB.
    Attach them to the stockMonitor.

  • Change the price of the stockMonitor.

  • We see that observerA and obsererB are both notified.

  • Detach observerA from the stockMonitor and change the stock price. We can see that only observerB is notified.

huangapple
  • 本文由 发表于 2010年9月17日 16:29:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/3733761.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定