Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Observer pattern in Go language

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

like image 675
Stas Avatar asked Sep 17 '10 08:09

Stas


People also ask

What is an Observer pattern give an example?

Observer is a behavioral design pattern. It specifies communication between objects: observable and observers. An observable is an object which notifies observers about the changes in its state. For example, a news agency can notify channels when it receives news.

What is meant by Observer pattern?

The observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

Is Observer pattern still used?

Yes, it is. The observer pattern is also called the publish/subscribe pattern, which is exactly what events allow you to do.

Is event listener an Observer pattern?

In Listener Design Pattern, when a event happens, the event raiser notifies a global mediator (the singleton Events. instance ), because it has no information about the event handlers. While in Observer Pattern, when any change happens, the observed subject directly notifies all the observers.


2 Answers

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 <- Msg
    }
}

func (s *Subscriber) ListenOnChannel() {
    for {
        data := <-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.

like image 64
cthom06 Avatar answered Dec 15 '22 15:12

cthom06


Here I give a classific implementation without channels, be free to refer this post enter image description here

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("StockObserver:", s.name, "has been updated,", "received subject string:", 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("Observer already exists")
        }
    }
    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("Observer not found")
}

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() {

    // Create a new stockMonitor object
    stockMonitor := &StockMonitor{
        ticker: "AAPL",
        price:  0.0,
    }

    observerA := &StockObserver{
        name: "Observer A",
    }
    observerB := &StockObserver{
        name: "Observer B",
    }

    // 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.
like image 44
Jerry An Avatar answered Dec 15 '22 14:12

Jerry An