Design patterns are encountered in both interviews and at work, but I often come across complaints from my partners that they have very little chance of applying design patterns in practice.
I have recently encountered a problem solving scenario using the Observer pattern at work, and I would like to share it with you.
The background is as follows.
Some additional things need to be done in the standard process after a user has created an order.
At the same time, these operations are not fixed, and logic can be added or modified at any time according to business development.
If the logic is written directly in the order placement business, this “heap” of not very core business will take up more and more space, and the normal order placement process may be affected when changes are made.
There are of course other options, such as starting a few timed tasks that regularly scan for orders and then implement their own business logic; however, this would waste many unnecessary requests.
The observer pattern is therefore born, where the event publisher notifies when its state changes and the observer gets the message to implement the business logic.
This allows the event publisher and receiver to be completely decoupled from each other; essentially an implementation of the open and closed principle.
Let’s start with a general look at the interfaces and relationships used by the Observer pattern.
- Subject interface: defines the interface for registering implementations, circular notifications.
- Observer interface: defines the interface for receiving notifications from the subject.
- Both the subject and observer interfaces can have multiple implementations.
- The business code only needs to use the
Next look at an example of the implementation of the order creation process.
The code is implemented in go and is similar in other languages.
Firstly, two interfaces are defined as per the above diagram.
Since we are placing an order, we have defined
OrderCreateSubject to implement
observerList slice is used to store all the observers who have subscribed to the order event.
Then it’s time to write the observer business logic, here I have implemented two.
It is also very simple to use.
- create a subject
- Register all subscription events.
- Call the
Notifymethod where a notification is required.
This way the individual event implementations will not affect each other should we need to change them, and even if we need to add other implementations it will be very easy to.
- write the implementation class.
- register into the entity.
No further changes are made to the core process.
In fact, we can also skip the step of registering events, that is, using containers; the general process is as follows.
- the custom events are all injected into the container.
- The place where the events are registered is where all the events are taken out of the container and registered one by one.
The container used here is https://github.com/uber-go/dig
In the modified code, whenever we add an observer (event subscription), we simply use the
Provide function provided by the container to register it into the container.
Also, in order for the container to support multiple instances of the same object, a new section of code has to be added
Two new structures are needed in the
observer interface to hold multiple instances of the same interface.
group: "observers"is used to declare that it is the same interface.
Instance object is returned when the concrete observer object is created.
It’s actually wrapped in Instance for once.
This allows all observer objects to be retrieved from
InstanceParams.Instances when an observer is registered.
This way, when used, the subject object is retrieved directly from the container and then notified of.
More information on the use of dig can be found in the official documentation at https://pkg.go.dev/go.uber.org/dig#hdr-Value_Groups
Experienced developers will find it very similar to the publish-subscribe model, but of course their thinking is similar; we don’t need to dwell on the differences (except in interviews); it’s more important to learn the thinking involved.