在Go语言中的属性变更通知

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

Property change notification in go

问题

你可以在Go语言中通过使用观察者模式来向多个接收器发出“属性”更改的信号。观察者模式是一种设计模式,用于在对象之间建立一对多的依赖关系,当一个对象的状态发生变化时,它的所有依赖对象都会收到通知。

在Go语言中,你可以使用接口和回调函数来实现观察者模式。首先,你需要定义一个接口,该接口包含一个用于接收属性更改通知的方法。然后,你可以在需要接收属性更改通知的结构体中实现该接口,并将其注册为观察者。

以下是一个示例代码:

package main

import "fmt"

// 定义观察者接口
type Observer interface {
    Update(value int)
}

// 定义可观察的属性结构体
type Property struct {
    observers []Observer
    value     int
}

// 注册观察者
func (p *Property) Attach(observer Observer) {
    p.observers = append(p.observers, observer)
}

// 取消注册观察者
func (p *Property) Detach(observer Observer) {
    for i, o := range p.observers {
        if o == observer {
            p.observers = append(p.observers[:i], p.observers[i+1:]...)
            break
        }
    }
}

// 更新属性值并通知观察者
func (p *Property) SetValue(value int) {
    p.value = value
    p.Notify()
}

// 通知观察者属性值的更改
func (p *Property) Notify() {
    for _, observer := range p.observers {
        observer.Update(p.value)
    }
}

// 实现观察者接口的结构体
type ProgressBar struct{}

func (pb ProgressBar) Update(value int) {
    fmt.Printf("Progress bar updated: %d%%\n", value)
}

type TextView struct{}

func (tv TextView) Update(value int) {
    fmt.Printf("Text view updated: %d%%\n", value)
}

func main() {
    // 创建可观察的属性
    progress := Property{}

    // 创建观察者
    progressBar := ProgressBar{}
    textView := TextView{}

    // 注册观察者
    progress.Attach(progressBar)
    progress.Attach(textView)

    // 更新属性值并通知观察者
    progress.SetValue(50)
}

在上面的示例代码中,我们定义了一个Property结构体作为可观察的属性,它包含一个observers切片用于存储观察者,以及一个value字段表示属性的值。我们还定义了一个Observer接口,其中包含一个Update方法用于接收属性更改通知。

main函数中,我们创建了一个progress属性实例以及一个ProgressBar和一个TextView观察者实例。然后,我们通过调用Attach方法将观察者注册到属性中。最后,我们通过调用SetValue方法更新属性值,并自动通知所有观察者。

当属性值发生更改时,观察者的Update方法将被调用,并传递新的属性值作为参数。在示例中,观察者只是简单地打印出属性值的更新信息,你可以根据实际需求进行相应的处理。

希望这可以帮助到你!

英文:

How can you signal 'property' changes to multiple receivers in go?

Similar to how you would define a property in Qt with a notify signal.

E.g. if you imagine having some value that needs to be shown in multiple ways, like a progress value that could be shown both as a progress bar and as textual %, where both would need to update when the underlying value changes.

答案1

得分: 5

一种方法是利用通道(channels)。

你的中央代码负责管理/更改需要监听的属性或变量,可以提供一个GetChan()函数,该函数返回一个通道,用于广播修改(例如新值):

// 被监听的变量或属性:
var i int

// 所有监听器的切片
var listeners []chan int

func GetChan() chan int {
    listener := make(chan int, 5)
    listeners = append(listeners, listener)
    return listener
}

每当你改变变量/属性时,你需要广播这个变化:

func Set(newi int) {
    i = newi
    for _, ch := range listeners {
        ch <- i
    }
}

监听器需要“监听”变化事件,可以通过对GetChan()返回的通道进行for range循环来实现:

func Background(name string, ch chan int, done chan int) {
    for v := range ch {
        fmt.Printf("[%s] 值发生变化:%d\n", name, v)
    }
    done <- 0
}

以下是主程序:

func main() {
    l1 := GetChan()
    l2 := GetChan()

    done := make(chan int)

    go Background("B1", l1, done)
    go Background("B2", l2, done)

    Set(3)
    time.Sleep(time.Second) // 等待一段时间
    Set(5)

    // 关闭所有监听器:
    for _, listener := range listeners {
        close(listener)
    }

    // 等待2个后台线程完成:
    <-done
    <-done
}

它的输出结果是:

[B1] 值发生变化:3
[B2] 值发生变化:3
[B1] 值发生变化:5
[B2] 值发生变化:5

你可以在Go Playground上尝试完整的程序。

你还可以实现一个“代理人(broker)”,实现订阅者模型并允许广播消息。参考:https://stackoverflow.com/questions/36417199/how-to-broadcast-message-using-channel/49877632#49877632

英文:

One way could to be to utilize channels.

Your central code which manages/changes the property or variable that needs to be listened may provide a GetChan() function which returns a channel on which modifications (e.g. new values) will be broadcasted:

// The variable or property that is listened:
var i int
// Slice of all listeners
var listeners []chan int
func GetChan() chan int {
listener := make(chan int, 5)
listeners = append(listeners, listener)
return listener
}

Whenever you change the variable/property, you need to broadcast the change:

func Set(newi int) {
i = newi
for _, ch := range listeners {
ch &lt;- i
}
}

And listeners need to "listen" for change events, which can be done by a for range loop on the channel returned by GetChan():

func Background(name string, ch chan int, done chan int) {
for v := range ch {
fmt.Printf(&quot;[%s] value changed: %d\n&quot;, name, v)
}
done &lt;- 0
}

Here is the main program:

func main() {
l1 := GetChan()
l2 := GetChan()
done := make(chan int)
go Background(&quot;B1&quot;, l1, done)
go Background(&quot;B2&quot;, l2, done)
Set(3)
time.Sleep(time.Second) // Wait a little
Set(5)
// Close all listeners:
for _, listener := range listeners {
close(listener)
}
// Wait 2 background threads to finish:
&lt;-done
&lt;-done
}

And its output:

[B1] value changed: 3
[B2] value changed: 3
[B1] value changed: 5
[B2] value changed: 5

You can try the complete program on the Go Playground.

You may also implement a "broker" which realizes a subscriber model and allows broadcasting messages. See https://stackoverflow.com/questions/36417199/how-to-broadcast-message-using-channel/49877632#49877632.

huangapple
  • 本文由 发表于 2015年4月7日 17:17:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/29488010.html
匿名

发表评论

匿名网友

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

确定