Observer Design Pattern in Go

1. Definition

The Observer Design Pattern defines a one-to-many dependency between objects so that when one object changes its state, all its dependents are notified and updated automatically.

2. Problem Statement

How can a subject provide updates to observers when its state changes, without knowing who or what those observers are?

3. Solution

The key idea is to have objects (observers) that wish to be informed about changes in another object (the subject). The observers subscribe to the subject, and whenever the subject undergoes a change, it notifies all of its observers.

4. Real-World Use Cases

1. News Publishers and Subscribers

2. Event management systems where listeners act on events.

3. Model-View-Controller (MVC) where the model notifies the view of changes.

5. Implementation Steps

1. Define the Observer interface that will be implemented by all concrete observers.

2. Define the Subject interface for all concrete subjects, with methods to attach, detach, and notify observers.

3. Create concrete classes for subjects and observers.

6. Implementation in Go

// Observer interface
type Observer interface {
	Update(message string)
}
// Subject interface
type Subject interface {
	RegisterObserver(o Observer)
	RemoveObserver(o Observer)
	NotifyObservers()
}
// Concrete Subject
type ConcreteSubject struct {
	observers   []Observer
	state       string
}
func NewConcreteSubject() *ConcreteSubject {
	return &ConcreteSubject{}
}
func (s *ConcreteSubject) RegisterObserver(o Observer) {
	s.observers = append(s.observers, o)
}
func (s *ConcreteSubject) RemoveObserver(o Observer) {
	var indexToRemove int
	for i, observer := range s.observers {
		if observer == o {
			indexToRemove = i
			break
		}
	}
	s.observers = append(s.observers[:indexToRemove], s.observers[indexToRemove+1:]...)
}
func (s *ConcreteSubject) NotifyObservers() {
	for _, observer := range s.observers {
		observer.Update(s.state)
	}
}
func (s *ConcreteSubject) SetState(state string) {
	s.state = state
	s.NotifyObservers()
}
// Concrete Observer
type ConcreteObserver struct {
	ID     string
}
func (o *ConcreteObserver) Update(message string) {
	fmt.Printf("Observer %s received message: %s\n", o.ID, message)
}
// Client code
func main() {
	subject := NewConcreteSubject()
	observer1 := &ConcreteObserver{ID: "O1"}
	observer2 := &ConcreteObserver{ID: "O2"}
	subject.RegisterObserver(observer1)
	subject.RegisterObserver(observer2)
	subject.SetState("New State!")
}

Output:

Observer O1 received message: New State!
Observer O2 received message: New State!

Explanation:

1. We define an Observer interface that all concrete observers will implement. It has an Update method to get notifications.

2. We define a Subject interface with methods for registering, removing, and notifying observers.

3. The ConcreteSubject maintains a list of observers and notifies them when its state changes.

4. The ConcreteObserver implements the Observer interface and acts when notified.

5. In the client code, we created a subject, registered two observers, and changed the subject's state, which led to the observers being notified.

7. When to use?

The Observer Pattern is useful when:

1. An object needs to inform other objects about changes without knowing who those objects are.

2. Components in a system are decoupled, where changes to one component can affect others without direct interaction.

3. An event mechanism is needed where listeners can subscribe and react to certain events.


Comments