Observer Pattern — Event System
Build a type-safe event system using the Observer pattern with generics and functional interfaces.
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
// Event base
interface Event {}
record UserCreated(String userId, String email) implements Event {}
record OrderPlaced(String orderId, double amount) implements Event {}
record PaymentProcessed(String orderId, String status) implements Event {}
// Event bus
class EventBus {
private final Map<Class<? extends Event>, List<Consumer<? extends Event>>> listeners = new HashMap<>();
@SuppressWarnings("unchecked")
public <T extends Event> void subscribe(Class<T> eventType, Consumer<T> listener) {
listeners.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
.add(listener);
}
@SuppressWarnings("unchecked")
public <T extends Event> void publish(T event) {
List<Consumer<? extends Event>> handlers = listeners.getOrDefault(event.getClass(), List.of());
for (var handler : handlers) {
((Consumer<T>) handler).accept(event);
}
}
}
// Services that listen to events
class EmailService {
public void onUserCreated(UserCreated event) {
System.out.printf("Sending welcome email to %s%n", event.email());
}
}
class AnalyticsService {
public void onOrderPlaced(OrderPlaced event) {
System.out.printf("Tracking order %s: $%.2f%n", event.orderId(), event.amount());
}
}
public class ObserverDemo {
public static void main(String[] args) {
EventBus bus = new EventBus();
EmailService emailService = new EmailService();
AnalyticsService analytics = new AnalyticsService();
// Subscribe
bus.subscribe(UserCreated.class, emailService::onUserCreated);
bus.subscribe(OrderPlaced.class, analytics::onOrderPlaced);
bus.subscribe(OrderPlaced.class, e ->
System.out.println("Inventory: reserving items for " + e.orderId()));
// Publish events
bus.publish(new UserCreated("U1", "alice@test.com"));
bus.publish(new OrderPlaced("ORD-1", 99.99));
}
}Use Cases
- Decoupled event-driven architectures
- Pub/sub systems within applications
- Domain event handling in DDD applications
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
In-Memory Event Bus — Pub/Sub Pattern
Build a type-safe in-memory event bus with subscribe, publish, and async event delivery.
Best for: Decoupled event-driven architectures
Builder Pattern — Fluent Object Construction
Implement the Builder pattern for complex objects with validation, immutability, and method chaining.
Best for: Constructing complex objects with many optional parameters
Singleton Pattern — Thread-Safe Approaches
Implement thread-safe singletons in Java: enum, holder class, double-checked locking, and eager init.
Best for: Application-wide configuration managers
Strategy Pattern with Lambdas
Implement the Strategy pattern using interfaces and Java lambdas for flexible algorithm selection.
Best for: Swappable pricing or discount algorithms