1. What is the Observer Pattern

The Observer Pattern is a behavioral design pattern used to establish a one-to-many dependency between objects. Specifically, when an object (referred to as the subject or observable) changes, all its dependencies (referred to as observers) are notified and updated automatically. This pattern allows for loose coupling between subjects and observers, thereby achieving decoupling and flexibility between objects.

2. Characteristics and Advantages of the Observer Pattern

The Observer Pattern has the following characteristics and advantages:

  • Loose coupling between subjects and observers, where the subject does not need to know the specific implementation details of the observers.
  • Dynamic addition and removal of observers, making the system more flexible.
  • Follows the open-closed principle between subjects and observers, enabling independent extension and reusability.
  • Can establish a one-to-many dependency relationship, where a subject can have multiple observers.

3. Examples of Practical Applications of the Observer Pattern

The Observer Pattern has many practical applications in real life, such as:

  • Event handling mechanism in GUI interfaces, like handling actions when a button is clicked.
  • Real-time stock market quotes push.
  • Notification of promotional activities on e-commerce platforms.

4. Implementation of the Observer Pattern in Golang

4.1 UML Class Diagram

Observer Pattern in Golang

4.2 Example Introduction

In this example, we have a subject (Subject) and two observers (ObserverA and ObserverB). The subject can register, unregister, and notify observers.

4.3 Implementation Steps

4.3.1 Create Subject Interface and Concrete Subject Class

type Subject interface {
    RegisterObserver(observer Observer)
    RemoveObserver(observer Observer)
    NotifyObservers()
}

type ConcreteSubject struct {
    observers []Observer
}

// Register listener object
func (subject *ConcreteSubject) RegisterObserver(observer Observer) {
    subject.observers = append(subject.observers, observer)
}

// Remove listener object
func (subject *ConcreteSubject) RemoveObserver(observer Observer) {
    for i, obs := range subject.observers {
        if obs == observer {
            subject.observers = append(subject.observers[:i], subject.observers[i+1:]...)
            break
        }
    }
}

// Trigger event notification
func (subject *ConcreteSubject) NotifyObservers() {
    for _, observer := range subject.observers {
        observer.Update()
    }
}

4.3.2 Create Observer Interface and Concrete Observer Classes

type Observer interface {
    Update()
}

type ConcreteObserverA struct {}

func (observer *ConcreteObserverA) Update() {
    fmt.Println("Observer A is notified.")
}

type ConcreteObserverB struct {}

func (observer *ConcreteObserverB) Update() {
    fmt.Println("Observer B is notified.")
}

4.4 Example Code Demo

func main() {
    subject := &ConcreteSubject{}
    observerA := &ConcreteObserverA{}
    observerB := &ConcreteObserverB{}

    subject.RegisterObserver(observerA)
    subject.RegisterObserver(observerB)

    subject.NotifyObservers()

    subject.RemoveObserver(observerA)

    subject.NotifyObservers()
}

Output:

Observer A is notified.
Observer B is notified.
Observer B is notified.

The above example code demonstrates the specific implementation of the observer pattern. The subject (ConcreteSubject) registered two observers (ObserverA and ObserverB), and then notified these two observers. After that, observer A was unregistered from the subject and the remaining observer B was notified again.