The schema definition

Define one to many dependencies between objects, let multiple observer objects listen to a subject object at the same time, when the subject object changes, all its dependents will receive notification and update

The class diagram

Application scenarios

Use the Observer pattern when changing the state of one object may require changing other objects, and the actual object is unknown beforehand or changing dynamically

advantages

1. Comply with the Open Closed Principle

2. Relationships between objects can be established at runtime

The point to summarize

  • Using object-oriented abstraction, Observe mode allows us to change the target and observer independently, thus loosely coupling the dependency between the two
  • When the target sends a notification, there is no need to specify an observer, and the notification (which can take the notification information as a parameter) is propagated automatically
  • The observer decides for himself whether he needs to subscribe to notifications, without the target knowing anything about it
  • The Observer pattern is a very common design pattern in event-based UI frameworks and is an important part of the MVC pattern

Go language code implementation

Project directory

observe.go

 package Observer
 ​
 import (
    "fmt"
    "sync"
    "time"
 )
 ​
 type Event struct {
    Data int
 }
 ​
 type Observer interface {
    NotifyCallback(event Event)
 }
 ​
 type Subject interface {
    AddListener(observer Observer)
    RemoveListener(observer Observer)
    Notify(event Event)
 }
 ​
 type eventObserver struct {
    ID   int
    Time time.Time
 }
 ​
 type eventSubject struct {
    Observers sync.Map
 }
 ​
 func (e eventObserver) NotifyCallback(event Event) {
    fmt.Printf("Recieved:%d after %v\n", event.Data, time.Since(e.Time))
 }
 ​
 func (e *eventSubject) AddListener(obs Observer) {
    e.Observers.Store(obs, struct{}{})
 }
 ​
 func (e *eventSubject) RemoveListener(obs Observer) {
    e.Observers.Delete(obs)
 }
 ​
 func (e *eventSubject) Notify(event Event) {
    e.Observers.Range(func(key, value interface{}) bool {
       if key == nil {
          return false
       }
       key.(Observer).NotifyCallback(event)
       return true
    })
 }
 ​
 func Fib(n int) chan int {
    out := make(chan int)
    go func() {
       defer close(out)
       for i, j := 0, 1; i < n; i, j = i+j, i {
          out <- i
       }
    }()
    return out
 }

observer_test.go

 package Observer
 ​
 import (
    "sync"
    "testing"
    "time"
 )
 ​
 func TestFib(t *testing.T) {
    //for x := range Fib(10) {
    // fmt.Println(x)
    //}
 ​
    n := eventSubject{Observers: sync.Map{}}
    obs1 := eventObserver{ID: 1, Time: time.Now()}
    obs2 := eventObserver{ID: 2, Time: time.Now()}
 ​
    n.AddListener(obs1)
    n.AddListener(obs2)
 ​
    for x := range Fib(10) {
       n.Notify(Event{Data: x})
    }
 }