javaintermediate

Observer Pattern — Event System

Build a type-safe event system using the Observer pattern with generics and functional interfaces.

java
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.