Skip to content

Instantly share code, notes, and snippets.

@thalesfsp
Created March 2, 2022 03:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thalesfsp/50c026de9c576980bb3c38f79d09a66a to your computer and use it in GitHub Desktop.
Save thalesfsp/50c026de9c576980bb3c38f79d09a66a to your computer and use it in GitHub Desktop.
Subject <-> Observer behavioural design pattern
// It is mainly used for implementing distributed event handling systems, in "event driven" software.
// Channels could have been used.
package main
//////
// Starts here.
//////
func main() {
// Create a new subject.
readinessSubject := NewSubject(false)
// Create observers.
apiObserver := NewObserver("API")
observerB := NewObserver("Status")
// Attach our Observers to readinessSubject.
readinessSubject.Attach(apiObserver)
readinessSubject.Attach(observerB)
// Start notification.
readinessSubject.Notify()
// Change readiness.
readinessSubject.SetReadiness(true)
// Change readiness again.
readinessSubject.SetReadiness(false)
// Change readiness again.
readinessSubject.SetReadiness(true)
}
package main
import (
"fmt"
)
//////
// Interfaces.
//////
// Observer actions definition.
type IObserver interface {
// Do something because the subject ready changed.
Do(string)
}
//////
// Object definition.
//////
type Observer struct {
// Name of the observer.
name string
}
//////
// IObserver implementation.
//////
// Do defines how to react when a subject changed.
func (s *Observer) Do(t bool) {
fmt.Printf(`%s observer has been notified. New readiness value "%v"`+"\n", s.name, t)
}
//////
// Factory.
//////
// NewObserver creates a new observer.
func NewObserver(name string) Observer {
return Observer{
name: name,
}
}
package main
import (
"errors"
)
//////
// Interfaces.
//////
// Subject actions definition.
type ISubject interface {
// Attach an Observer to observe the subject.
Attach(o Observer) (bool, error)
// Detach an Observer which is observing the subject.
Detach(o Observer) (bool, error)
// Notify observers of a change.
//
// NOTE: This is why it's pattern from the behavioral category.
Notify() (bool, error)
}
//////
// Object definition.
//////
type Subject struct {
// Could be anything. For flexibility, could be an `interface{}`.
// It would require type casting.
ready bool
// List of observers.
observers []Observer
}
//////
// ISubject implementation.
//////
// Attach an observer to observe the subject.
func (s *Subject) 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
}
// Detach an observer which is observing the subject.
func (s *Subject) 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")
}
// Notify observers of a change.
func (s *Subject) Notify() (bool, error) {
for _, observer := range s.observers {
observer.Do(s.ready)
}
return true, nil
}
//////
// Accessors.
//////
// SetReadiness update readiness.
func (s *Subject) SetReadiness(ready bool) {
s.ready = ready
// Notifies all observers of the change.
s.Notify()
}
//////
// Factory.
//////
// NewSubject creates a new subject.
func NewSubject(ready bool) Subject {
return Subject{
ready: ready,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment