SoFunction
Updated on 2025-03-05

An investigation into the example of Golang Observer Mode Optimization Order Processing System

What is the Observer Design Pattern?

The observer design pattern is a behavioral design pattern that allows loosely coupled communication between objects. In this pattern, an object (called the Subject) maintains a list of objects that depend on it (called the Observer Observer) and automatically notifies them when the state changes. The observer pattern can be used to implement an event-driven system where interactions between objects are accomplished through publishing and subscribing to events.

Application scenario: Order processing system

In an order processing system, the status of the order may change, such as creation, payment, shipment, cancellation, etc. When the order status changes, we hope to notify relevant observers to perform corresponding processing, such as updating inventory, sending notifications, etc.

Implement the observer design model

In Golang, channels and goroutines can be used to implement observer design patterns.

First, we define an order structure that contains the basic information and status of the order:

type Order struct {
    ID     string
    Status string
}

Then, we define an observer interface, including aUpdateMethod, used to handle notification of order status changes:

type Observer interface {
    Update(order *Order, wg *)
}

Next, we define a theme structure, using channel and goroutine to notify observers:

type Subject struct {
    observers []Observer
}
func (s *Subject) Register(observer Observer) {
     = append(, observer)
}
func (s *Subject) Notify(order *Order) {
    wg := {}
    (len())
    errCh := make(chan error, len())
    for _, observer := range  {
        go func(obs Observer) {
            defer ()
            err := (order, &wg)
            if err != nil {
                errCh <- err
            }
        }(observer)
    }
    ()
    close(errCh)
    // Handle exceptions    for err := range errCh {
        ("Error occurred:", err)
    }
}

We first created aerrCh(Typechan error) to receive exceptions that may occur during the observer's processing. Then, in each observer's goroutine we pass it through closureobserverAnd check whether any exceptions have occurred after the processing is completed. If there is an exception, we send it toerrChmiddle.

existNotifyAt the end of the method, we closed iterrChchannel and passrangeLoop to handle all exceptions.

Next, we achieve two observers: inventory observer and notification observer.

Inventory Observer is used to update inventory status:

type InventoryObserver struct{}
func (io *InventoryObserver) Update(order *Order, wg *) {
    defer ()
    // Update inventory status    ("Inventory Observer: Order %s status changed to %s\n", , )
}

Notification observers are used to send notifications:

type NotificationObserver struct{}
func (no *NotificationObserver) Update(order *Order, wg *) {
    defer ()
    // Send notification    ("Notification Observer: Order %s status changed to %s\n", , )
}

Finally, we use observer pattern in the main function to handle notifications of order state changes:

func main() {
    order := &Order{
        ID:     "123",
        Status: "Created",
    }
    subject := &Subject{}
    (&InventoryObserver{})
    (&NotificationObserver{})
    // Simulate order status changes     = "Paid"
    (order)
     = "Shipped"
    (order)
}

We created an order object and a topic object and registered the inventory observer. Then, we simulate the change in the order status, by callingNotifyThe method concurrently notifies the observer for processing.

By using channel and goroutine, we can implement concurrent processing of observer mode to improve the performance and responsiveness of the system.

Some advantages of using observer design patterns

  • 1. Loose coupling: Observer mode can decouple observer and subject (or by observer) objects. The observer only needs to pay attention to the state changes of the subject, not to understand the specific implementation details. This can make the system more flexible and scalable.
  • 2. Reusability: By separating observers and subject objects, they can be reused in different contexts. For example, the same observer can be used in different business scenarios to handle different topic objects.
  • 3. Easy to expand: Observer mode can be easily expanded when new observers or topics are needed. Just implement a new observer or topic object and register it in the topic object.
  • 4. Event-driven: Observer mode is suitable for event-driven systems. When the state of the subject object changes, all observers can be notified to perform corresponding processing by triggering events.

If you do not use the Observer Design Pattern

The order business may be implemented in a tighter coupling way. Here is a sample code showing how to deal with order status changes without using observer mode:

type Order struct {
    ID     string
    Status string
}
type OrderProcessor struct {
    inventoryObserver    *InventoryObserver
    notificationObserver *NotificationObserver
}
func NewOrderProcessor() *OrderProcessor {
    return &OrderProcessor{
        inventoryObserver:    &InventoryObserver{},
        notificationObserver: &NotificationObserver{},
    }
}
func (op *OrderProcessor) Process(order *Order) {
    // Update inventory    (order)
    // Send notification    (order)
}
func main() {
    order := &Order{
        ID:     "123",
        Status: "Created",
    }
    op := NewOrderProcessor()
    // Simulate order status changes     = "Paid"
    (order)
     = "Shipped"
    (order)
}

In this example,OrderProcessorThe object is responsible for handling order status changes. It containsInventoryObserverandNotificationObserverobject, and inProcessThe method calls them in sequenceUpdateMethod to handle order status changes.

There are some problems with this implementation:

1. Tight coupling:OrderProcessorThe object depends directly onInventoryObserverandNotificationObserverObject. If you need to add or delete other observers, you need to modify themOrderProcessorcode, resulting in reduced maintainability and scalability of the code.

2. Code duplication: Whenever there is a new observer that needs to handle the order status changes, it is necessary toOrderProcessorAdd the corresponding code to it. This will lead to duplication and redundancy of the code.

3. Poor scalability: It is difficult to add new observers to the system without using observer mode, because it needs to be modified every timeOrderProcessorcode.

The above is the detailed content of the example exploration of the Golang Observer Mode optimization order processing system. For more information about the Golang Observer Mode, please pay attention to my other related articles!