# How to Combine Observer and Mediator Patterns for Complex Event Systems in Java

> Learn how to combine Observer and Mediator patterns in Java. Route subject notifications via a Mediator hub to coordinate Observers and eliminate direct dependencies in complex event systems.

- Repository: [Ilkka Seppälä/java-design-patterns](https://github.com/iluwatar/java-design-patterns)
- Tags: deep-dive
- Published: 2026-02-27

---

**Combine Observer and Mediator patterns by routing Observable subject notifications through a Mediator hub that coordinates dispatch to concrete Observers, eliminating direct dependencies between event sources and consumers.**

When building enterprise Java applications with multiple event sources and cross-cutting concerns, combining the Observer and Mediator patterns creates a robust, maintainable architecture for complex event systems. The Observer pattern handles state change notifications while the Mediator pattern centralizes coordination logic, preventing the tight coupling that typically emerges in large codebases. The `iluwatar/java-design-patterns` repository demonstrates this integration through its generic `Observable` framework and `Party` mediator implementation.

## Core Pattern Implementations

### Observer Pattern Foundation

In [`observer/src/main/java/com/iluwatar/observer/generic/Observable.java`](https://github.com/iluwatar/java-design-patterns/blob/main/observer/src/main/java/com/iluwatar/observer/generic/Observable.java), the base class provides the subscription mechanism through `addObserver()`, `removeObserver()`, and `notifyObservers()` methods. Concrete subjects extend this class to publish state changes without knowing their observers. The corresponding [`Observer.java`](https://github.com/iluwatar/java-design-patterns/blob/main/Observer.java) interface defines the `update(S subject, O observer, A argument)` contract that receivers must implement.

### Mediator Pattern Coordination

The Mediator pattern implementation lives in [`mediator/src/main/java/com/iluwatar/mediator/Party.java`](https://github.com/iluwatar/java-design-patterns/blob/main/mediator/src/main/java/com/iluwatar/mediator/Party.java) and [`PartyImpl.java`](https://github.com/iluwatar/java-design-patterns/blob/main/PartyImpl.java). The `Party` interface acts as the central coordinator, while `PartyImpl` maintains a registry of `PartyMember` colleagues. When one member calls `act(Action action)`, the mediator routes the action to all other registered members, ensuring colleagues communicate only through the `Party` abstraction.

## Architectural Integration Strategy

### Mediator as Observer Hub

To combine Observer and Mediator patterns effectively, configure the Mediator as the central hub for Observer registration. Instead of subjects notifying observers directly, Observable subjects delegate to the Mediator, which then applies routing logic before dispatching to concrete Observers. This approach consolidates event distribution logic in a single location while maintaining the **loose coupling** benefits of both patterns.

### Decoupling Event Sources

Observable subjects maintain only a reference to the Mediator interface rather than individual observer collections. When state changes occur, the subject invokes the mediator's coordination method, allowing the Mediator to filter, transform, or prioritize events before broadcasting to registered colleagues like `Wizard` or `Hunter`. This structure ensures that adding new observer types requires changes only to the Mediator's registration logic, not modifications to existing Observable subjects.

## Implementation Walkthrough

### Key Source Files

The integration relies on these specific files from the `iluwatar/java-design-patterns` repository:

- **[`observer/src/main/java/com/iluwatar/observer/generic/Observable.java`](https://github.com/iluwatar/java-design-patterns/blob/main/observer/src/main/java/com/iluwatar/observer/generic/Observable.java)**: Base class providing `notifyObservers()` for subject state changes
- **[`observer/src/main/java/com/iluwatar/observer/generic/Observer.java`](https://github.com/iluwatar/java-design-patterns/blob/main/observer/src/main/java/com/iluwatar/observer/generic/Observer.java)**: Generic interface requiring `update(S subject, A argument)` implementation  
- **[`mediator/src/main/java/com/iluwatar/mediator/Party.java`](https://github.com/iluwatar/java-design-patterns/blob/main/mediator/src/main/java/com/iluwatar/mediator/Party.java)**: Mediator contract with `addMember()` and `act()` methods
- **[`mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java`](https://github.com/iluwatar/java-design-patterns/blob/main/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java)**: Concrete mediator implementing event routing logic
- **[`mediator/src/main/java/com/iluwatar/mediator/Wizard.java`](https://github.com/iluwatar/java-design-patterns/blob/main/mediator/src/main/java/com/iluwatar/mediator/Wizard.java)**: Example concrete observer implementing `PartyMember`

### Wiring Observable Subjects with Mediator Coordination

The following example demonstrates combining a weather-tracking Observable with the Party Mediator to coordinate game character reactions:

```java
// Event payload definition
public record WeatherEvent(String condition, int temperature) {}

// Observable subject extending the generic framework
public class WeatherStation
    extends Observable<WeatherStation, WeatherObserver, WeatherEvent> {
  
  public void changeWeather(String condition, int temp) {
    // Business logic for state change...
    notifyObservers(new WeatherEvent(condition, temp));
  }
}

// Observer interface specialization
public interface WeatherObserver
    extends Observer<WeatherStation, WeatherObserver, WeatherEvent> {}

// Concrete observer implementing both patterns
public class GameCharacter implements WeatherObserver, PartyMember {
  private final Party mediator;

  public GameCharacter(Party mediator) {
    this.mediator = mediator;
  }

  @Override
  public void update(WeatherStation subject, WeatherEvent event) {
    // Translate domain event to mediator action
    Action action = switch (event.condition()) {
      case "rain" -> Action.ATTACK;
      case "sunny" -> Action.DEFEND;
      default -> Action.HEAL;
    };
    mediator.act(this, action);  // Delegate coordination to Mediator
  }

  @Override
  public void partyAction(Action action) {
    System.out.println(getClass().getSimpleName() + " performs " + action);
  }

  @Override
  public void joinedParty(Party party) {
    System.out.println(getClass().getSimpleName() + " joins the party");
  }
}

// Application wiring
public class Main {
  public static void main(String[] args) {
    PartyImpl party = new PartyImpl();              // Mediator hub
    WeatherStation weather = new WeatherStation();  // Observable subject

    GameCharacter wizard = new GameCharacter(party);
    GameCharacter hunter = new GameCharacter(party);

    // Register with Observer pattern
    weather.addObserver(wizard);
    weather.addObserver(hunter);

    // Register with Mediator pattern
    party.addMember(wizard);
    party.addMember(hunter);

    // Trigger complex event propagation
    weather.changeWeather("rain", 12);
    weather.changeWeather("sunny", 25);
  }
}

```

In this implementation, `WeatherStation` extends `Observable` to notify registered `WeatherObserver` instances. Each `GameCharacter` receives the weather update through the Observer interface's `update()` method, translates the event into a game `Action`, and delegates to the `Party` Mediator. The `PartyImpl` mediator then coordinates the action among all party members except the initiator, achieving complex event propagation without direct coupling between the weather system and character logic.

## Benefits of Combining Observer and Mediator Patterns

**Loose Coupling**: Subjects depend only on the Mediator interface, not concrete observers, while observers receive events through standardized interfaces defined in [`Observable.java`](https://github.com/iluwatar/java-design-patterns/blob/main/Observable.java) and [`Party.java`](https://github.com/iluwatar/java-design-patterns/blob/main/Party.java).

**Centralized Control**: The Mediator consolidates routing logic, enabling filtering, transformation, and prioritization of events before dispatch, as implemented in [`PartyImpl.java`](https://github.com/iluwatar/java-design-patterns/blob/main/PartyImpl.java)'s action distribution logic.

**Scalability**: Adding new event types or observers requires changes only to the Mediator's registration logic, not modifications to existing Observable subjects or concrete observers.

**Testability**: Observers can be unit-tested in isolation by mocking the Mediator interface, while the Mediator's routing logic can be verified independently of concrete subject implementations.

## Summary

- Combine Observer and Mediator patterns by using the Mediator as a central hub that receives Observable notifications and coordinates observer dispatch to eliminate direct dependencies.
- The `Observable` class in [`observer/src/main/java/com/iluwatar/observer/generic/Observable.java`](https://github.com/iluwatar/java-design-patterns/blob/main/observer/src/main/java/com/iluwatar/observer/generic/Observable.java) provides the subscription mechanism, while `PartyImpl` in [`mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java`](https://github.com/iluwatar/java-design-patterns/blob/main/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java) handles coordination.
- Concrete classes implement both `Observer` and `PartyMember` interfaces to bridge pattern boundaries, translating domain events into mediator actions.
- This architecture keeps subjects decoupled from observers while maintaining centralized control over event distribution logic.
- The approach scales effectively for complex systems like game engines, UI frameworks, and IoT platforms requiring multi-layered event handling.

## Frequently Asked Questions

### How do Observer and Mediator patterns differ in event handling?

The Observer pattern establishes a one-to-many dependency between subjects and observers where subjects notify observers directly of state changes. The Mediator pattern introduces a central coordinator that handles all interactions between colleagues, preventing them from referring to each other explicitly. When combining both, the Mediator acts as an intermediary layer that receives Observer notifications and applies business logic before dispatching to final consumers.

### Can the Mediator replace the Observer pattern entirely?

No, the Mediator and Observer patterns serve complementary purposes. The Observer pattern excels at broadcasting state changes to unknown subscribers, while the Mediator excels at coordinating complex interactions between known colleagues. In the `iluwatar/java-design-patterns` implementation, the Mediator relies on the Observer pattern's notification mechanism while adding coordination logic that pure Observer cannot provide, such as filtering events or coordinating multi-step workflows between `PartyMember` instances.

### What are the performance implications of adding a Mediator layer?

Adding a Mediator introduces minimal overhead—typically a single method call and collection iteration—but provides significant architectural benefits. The `PartyImpl` mediator in the repository maintains a simple `List<PartyMember>` and iterates during `act()` calls, resulting in O(n) complexity for event distribution. This cost is negligible compared to the maintenance burden of direct observer-to-subject dependencies in complex systems, and the Mediator enables optimizations like event batching or asynchronous dispatch that would be difficult to implement in a pure Observer architecture.

### How do I handle different event types when combining these patterns?

Define specific event records or classes (like `WeatherEvent` in the example) and use the Mediator to route based on event type or content. The Mediator's `act()` method can inspect the event payload and delegate to specialized handler methods, or you can create multiple Mediator implementations for different event categories. In the provided example, the `GameCharacter` class translates `WeatherEvent` into `Action` enums before calling the Mediator, demonstrating how domain-specific events can be normalized into the Mediator's coordination protocol.