Observer design pattern is a one-to-many push-notification design, where the object notify all its observing objects when it experiences a change or an event. This is commonly seen in:
The diagram is as follows:
Here is an example in Go. Unlike other languages, Go requires one to create the Communicator
structure that acts as the communication abstracts between 2 objects. It itself is a composite patterned structure where it has many subscribers, which is itself. The Communicator structure offers the Subscribe
, Unsubscribe
, Notify
, and Update
functions for the event notification and responding purposes.
In MVC, MVVM, or VIPER framework, all 4 event functions above are best known as:
Subscribe
➔ AddEventListener
Unsubscribe
➔ RemoveEventListener
/ DeleteEventListener
Notify
➔ ActionPerformed
Update
➔ ActionEvent
package main
import (
"fmt"
)
/*********************************************************************
* INTERFACE - COMMUNICATOR *
********************************************************************/
// Communicator structure for managing subscriptions and notifications
type Communicator struct {
Label string
subscribers []*Communicator
action func(interface{})
}
// New Communicator creates a Communicator object
func NewCommunicator(label string) *Communicator {
return &Communicator{
Label: label,
subscribers: []*Communicator{},
action: nil,
}
}
// Subscribe is for the communicator to add another communicator
func (c *Communicator) Subscribe(ch *Communicator) {
if ch != nil {
fmt.Printf("%v event: %v subbed.\n", c.Label, ch.Label)
c.subscribers = append(c.subscribers, ch)
}
}
// Unsubscribe is for the communicator to remove another communicator
func (c *Communicator) Unsubscribe(ch *Communicator) {
if ch == nil {
return
}
for i, s := range c.subscribers {
if s == ch {
fmt.Printf("%v event: %v unsubbed.\n", c.Label, s.Label)
c.subscribers = append(c.subscribers[:i],
c.subscribers[i+1:]...)
break
}
}
}
// Notify is for the communicator to notify all its subscribers with an
// event.
func (c *Communicator) Notify(event interface{}) {
for _, s := range c.subscribers {
s.Update(event)
}
}
// Update runs the executions when the communicator is notified
func (c *Communicator) Update(event interface{}) {
if c.action != nil {
c.action(event)
}
}
// Register adds an action to the communicator for Update to execute
func (c *Communicator) Register(fx func(interface{})) {
c.action = fx
}
/*********************************************************************
* SUBJECT CONCRETE *
********************************************************************/
// Subject is the main even trigger. It creates the Channel communicator.
type Subject struct {
Channel *Communicator
}
func NewSubject(label string) *Subject {
return &Subject{
Channel: NewCommunicator(label),
}
}
func (s *Subject) PressedButton() {
fmt.Printf("Subject: user pressed button\n")
s.Channel.Notify("Button pressed")
}
/*********************************************************************
* OBSERVER CONCRETE *
********************************************************************/
// Observer is the main subscriber to the subject. It has its own
// Channel.
type Object struct {
Channel *Communicator
}
func NewObject(label string) *Object {
o := Object{
Channel: NewCommunicator(label),
}
o.Channel.action = o.Update
return &o
}
func (o *Object) Update(event interface{}) {
fmt.Printf("%v Update: %v\n", o.Channel.Label, event)
}
/*********************************************************************
* MAIN ACTION *
********************************************************************/
func main() {
s := NewSubject("subject 1")
o1 := NewObject("object 1")
o2 := NewObject("object 2")
// o1 and o2 observes s
s.Channel.Subscribe(o1.Channel)
s.Channel.Subscribe(o2.Channel)
// s received an event
s.PressedButton()
// o2 stop observing
s.Channel.Unsubscribe(o2.Channel)
// s received an event
s.PressedButton()
}
// Output:
// subject 1 event: object 1 subbed.
// subject 1 event: object 2 subbed.
// Subject: user pressed button
// object 1 Update: Button pressed
// object 2 Update: Button pressed
// subject 1 event: object 2 unsubbed.
// Subject: user pressed button
// object 1 Update: Button pressed
That's all about observer design pattern.