Observer Pattern: Decoupling Order Payment Success Actions via Publish‑Subscribe
The article explains the Observer pattern by first showing the problems of hard‑coding post‑payment actions, then detailing how defining a subject, observer interface, and concrete observers decouples the system, discusses push vs pull notification models, and illustrates real‑world uses in Spring events, GUI listeners, and message queues.
1. Hard‑coding post‑payment actions
When an order is paid, a naïve implementation puts every downstream action—adding points, sending SMS, notifying logistics, updating sales statistics—directly inside OrderService.paySuccess. This creates:
Tight coupling : OrderService must know every downstream service.
Violation of the Open/Closed Principle : adding a new action requires modifying the method.
Mixed responsibilities : the method handles payment logic and unrelated side‑effects, some of which may be asynchronous or need retry.
public class OrderService {
public void paySuccess(Order order) {
// after payment, notify downstream services
pointService.addPoints(order); // add points
smsService.send(order); // send SMS
logisticsService.prepare(order); // notify logistics
statService.updateSales(order); // update sales stats
// tomorrow we may add "send coupon" ...
}
}The root cause is that the event producer (payment success) and the event consumers are hard‑coded together.
2. Observer pattern: subscription and notification
Step 1 – Define the observer interface :
// Observer: all downstream actions implement this
public interface OrderObserver {
void onPaySuccess(Order order);
}Step 2 – Implement concrete observers :
public class PointObserver implements OrderObserver {
public void onPaySuccess(Order order) { /* add points */ }
}
public class SmsObserver implements OrderObserver {
public void onPaySuccess(Order order) { /* send SMS */ }
}
public class LogisticsObserver implements OrderObserver {
public void onPaySuccess(Order order) { /* notify logistics */ }
}Step 3 – Subject maintains the observer list and notifies :
public class OrderSubject {
// observer list
private final List<OrderObserver> observers = new ArrayList<>();
public void subscribe(OrderObserver o) { observers.add(o); }
public void unsubscribe(OrderObserver o) { observers.remove(o); }
// when payment succeeds, broadcast to all observers
public void paySuccess(Order order) {
// ... handle payment itself
for (OrderObserver o : observers) {
o.onPaySuccess(order); // notify each observer
}
}
}Usage:
OrderSubject subject = new OrderSubject();
subject.subscribe(new PointObserver()); // points
subject.subscribe(new SmsObserver()); // SMS
subject.subscribe(new LogisticsObserver()); // logistics
subject.paySuccess(order); // one call, all observers reactCompared with the hard‑coded version, OrderSubject no longer knows concrete downstream services; it only depends on the OrderObserver abstraction, satisfying the Open/Closed Principle.
3. Roles and push vs. pull models
Abstract Subject – e.g. OrderSubject (can be an interface). Maintains the observer list and provides subscribe/unsubscribe methods.
Concrete Subject – the actual implementation that notifies observers when state changes.
Abstract Observer – the OrderObserver interface that defines the event‑handling method.
Concrete Observer – classes such as PointObserver, SmsObserver, LogisticsObserver that implement specific response logic.
The key design choice is how much data the subject passes to observers:
Push model : the subject pushes full data, e.g. void onPaySuccess(Order order). Simple but may transmit unnecessary information.
Pull model : the subject only signals a change, e.g. void onEvent(OrderSubject subject). Observers pull what they need via methods on the subject, offering flexibility at the cost of an extra step.
// Push model
void onPaySuccess(Order order); // receives all data
// Pull model
void onEvent(OrderSubject subject); // observer calls subject.getXxx()Choose push when observers need essentially the same data; choose pull when data requirements vary or are large.
4. Real‑world appearances
Spring events (ApplicationEvent + ApplicationListener): publishing new OrderPaidEvent(order) notifies all @EventListener methods, optionally async with @Async.
GUI listeners : button click listeners in Swing ( addActionListener) or front‑end addEventListener follow the same pattern.
Message‑queue pub‑sub : Kafka, RabbitMQ topics broadcast to all subscribed consumers.
JDK Observer : java.util.Observer / Observable (deprecated since Java 9).
5. When to use and when not to use
Use when:
An object's state change must automatically notify a dynamically changing set of other objects.
The producer should be decoupled from the consumers.
There is a natural one‑to‑many dependency.
Do not use when:
The post‑action is fixed and never changes (over‑engineering).
Consumers have strict ordering or need each other's results (observers assume parallel independence).
6. Common pitfalls
Notification order is not guaranteed : observers are usually notified in subscription order, but code should not rely on it.
Exception propagation : a synchronous loop will stop notifying remaining observers if one throws; handle exceptions per observer or use asynchronous notification.
Memory leaks : forgetting to unsubscribe keeps the subject holding references, preventing garbage collection.
7. Summary
The Observer pattern solves the “one change, many know” problem by letting the subject maintain an observer list and broadcast changes without knowing concrete observers. It supports both push (simple, direct data transfer) and pull (flexible, on‑demand) models. Spring events, GUI listeners, and message‑queue pub‑sub are practical incarnations. Apply it when a dynamic one‑to‑many notification relationship exists, and watch out for ordering, exception handling, and memory‑leak risks.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
