Compare commits

..

2 Commits

13 changed files with 32 additions and 231 deletions

View File

@ -1767,15 +1767,6 @@
"contributions": [
"code"
]
},
{
"login": "Leisterbecker",
"name": "Leisterbecker",
"avatar_url": "https://avatars.githubusercontent.com/u/20650323?v=4",
"profile": "https://github.com/Leisterbecker",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@ -10,7 +10,7 @@
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=coverage)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
[![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-195-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-194-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<br/>
@ -324,7 +324,6 @@ This project is licensed under the terms of the MIT license.
<td align="center"><a href="https://github.com/Shrirang97"><img src="https://avatars.githubusercontent.com/u/28738668?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shrirang</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AShrirang97" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=Shrirang97" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/interactwithankush"><img src="https://avatars.githubusercontent.com/u/18613127?v=4?s=100" width="100px;" alt=""/><br /><sub><b>interactwithankush</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=interactwithankush" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/yuhangbin"><img src="https://avatars.githubusercontent.com/u/17566866?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CharlieYu</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yuhangbin" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Leisterbecker"><img src="https://avatars.githubusercontent.com/u/20650323?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leisterbecker</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Leisterbecker" title="Code">💻</a></td>
</tr>
</table>

View File

@ -9,10 +9,6 @@ tags:
- Reactive
---
## Name
Event Aggregator
## Intent
A system with lots of objects can lead to complexities when a
client wants to subscribe to events. The client has to find and register for
@ -21,136 +17,6 @@ requires a separate subscription. An Event Aggregator acts as a single source
of events for many objects. It registers for all the events of the many objects
allowing clients to register with just the aggregator.
## Explanation
Real-world example
> King Joffrey sits on the iron throne and rules the seven kingdoms of Westeros. He receives most
> of his critical information from King's Hand, the second in command. King's hand has many
> close advisors himself, feeding him with relevant information about events occurring in the
> kingdom.
In Plain Words
> Event Aggregator is an event mediator that collects events from multiple sources and delivers
> them to registered observers.
**Programmatic Example**
In our programmatic example, we demonstrate the implementation of an event aggregator pattern. Some of
the objects are event listeners, some are event emitters, and the event aggregator does both.
```java
public interface EventObserver {
void onEvent(Event e);
}
public abstract class EventEmitter {
private final Map<Event, List<EventObserver>> observerLists;
public EventEmitter() {
observerLists = new HashMap<>();
}
public final void registerObserver(EventObserver obs, Event e) {
...
}
protected void notifyObservers(Event e) {
...
}
}
```
`KingJoffrey` is listening to events from `KingsHand`.
```java
@Slf4j
public class KingJoffrey implements EventObserver {
@Override
public void onEvent(Event e) {
LOGGER.info("Received event from the King's Hand: {}", e.toString());
}
}
```
`KingsHand` is listening to events from his subordinates `LordBaelish`, `LordVarys`, and `Scout`.
Whatever he hears from them, he delivers to `KingJoffrey`.
```java
public class KingsHand extends EventEmitter implements EventObserver {
public KingsHand() {
}
public KingsHand(EventObserver obs, Event e) {
super(obs, e);
}
@Override
public void onEvent(Event e) {
notifyObservers(e);
}
}
```
For example, `LordVarys` finds a traitor every Sunday and notifies the `KingsHand`.
```java
@Slf4j
public class LordVarys extends EventEmitter implements EventObserver {
@Override
public void timePasses(Weekday day) {
if (day == Weekday.SATURDAY) {
notifyObservers(Event.TRAITOR_DETECTED);
}
}
}
```
The following snippet demonstrates how the objects are constructed and wired together.
```java
var kingJoffrey = new KingJoffrey();
var kingsHand = new KingsHand();
kingsHand.registerObserver(kingJoffrey, Event.TRAITOR_DETECTED);
kingsHand.registerObserver(kingJoffrey, Event.STARK_SIGHTED);
kingsHand.registerObserver(kingJoffrey, Event.WARSHIPS_APPROACHING);
kingsHand.registerObserver(kingJoffrey, Event.WHITE_WALKERS_SIGHTED);
var varys = new LordVarys();
varys.registerObserver(kingsHand, Event.TRAITOR_DETECTED);
varys.registerObserver(kingsHand, Event.WHITE_WALKERS_SIGHTED);
var scout = new Scout();
scout.registerObserver(kingsHand, Event.WARSHIPS_APPROACHING);
scout.registerObserver(varys, Event.WHITE_WALKERS_SIGHTED);
var baelish = new LordBaelish(kingsHand, Event.STARK_SIGHTED);
var emitters = List.of(
kingsHand,
baelish,
varys,
scout
);
Arrays.stream(Weekday.values())
.<Consumer<? super EventEmitter>>map(day -> emitter -> emitter.timePasses(day))
.forEachOrdered(emitters::forEach);
```
The console output after running the example.
```
18:21:52.955 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: Warships approaching
18:21:52.960 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: White walkers sighted
18:21:52.960 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: Stark sighted
18:21:52.960 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: Traitor detected
```
## Class diagram
![alt text](./etc/classes.png "Event Aggregator")
@ -160,13 +26,9 @@ Use the Event Aggregator pattern when
* Event Aggregator is a good choice when you have lots of objects that are
potential event sources. Rather than have the observer deal with registering
with them all, you can centralize the registration logic to the Event
Aggregator. As well as simplifying registration, an Event Aggregator also
Aggregator. As well as simplifying registration, a Event Aggregator also
simplifies the memory management issues in using observers.
## Related patterns
* [Observer](https://java-design-patterns.com/patterns/observer/)
## Credits
* [Martin Fowler - Event Aggregator](http://martinfowler.com/eaaDev/EventAggregator.html)

View File

@ -49,28 +49,13 @@ public class App {
public static void main(String[] args) {
var kingJoffrey = new KingJoffrey();
var kingsHand = new KingsHand();
kingsHand.registerObserver(kingJoffrey, Event.TRAITOR_DETECTED);
kingsHand.registerObserver(kingJoffrey, Event.STARK_SIGHTED);
kingsHand.registerObserver(kingJoffrey, Event.WARSHIPS_APPROACHING);
kingsHand.registerObserver(kingJoffrey, Event.WHITE_WALKERS_SIGHTED);
var varys = new LordVarys();
varys.registerObserver(kingsHand, Event.TRAITOR_DETECTED);
varys.registerObserver(kingsHand, Event.WHITE_WALKERS_SIGHTED);
var scout = new Scout();
scout.registerObserver(kingsHand, Event.WARSHIPS_APPROACHING);
scout.registerObserver(varys, Event.WHITE_WALKERS_SIGHTED);
var baelish = new LordBaelish(kingsHand, Event.STARK_SIGHTED);
var kingsHand = new KingsHand(kingJoffrey);
var emitters = List.of(
kingsHand,
baelish,
varys,
scout
new LordBaelish(kingsHand),
new LordVarys(kingsHand),
new Scout(kingsHand)
);
Arrays.stream(Weekday.values())

View File

@ -31,7 +31,6 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum Event {
WHITE_WALKERS_SIGHTED("White walkers sighted"),
STARK_SIGHTED("Stark sighted"),
WARSHIPS_APPROACHING("Warships approaching"),
TRAITOR_DETECTED("Traitor detected");

View File

@ -23,48 +23,31 @@
package com.iluwatar.event.aggregator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* EventEmitter is the base class for event producers that can be observed.
*/
public abstract class EventEmitter {
private final Map<Event, List<EventObserver>> observerLists;
private final List<EventObserver> observers;
public EventEmitter() {
observerLists = new HashMap<>();
observers = new LinkedList<>();
}
public EventEmitter(EventObserver obs, Event e) {
public EventEmitter(EventObserver obs) {
this();
registerObserver(obs, e);
registerObserver(obs);
}
/**
* Registers observer for specific event in the related list.
*
* @param obs the observer that observers this emitter
* @param e the specific event for that observation occurs
* */
public final void registerObserver(EventObserver obs, Event e) {
if (!observerLists.containsKey(e)) {
observerLists.put(e, new LinkedList<>());
}
if (!observerLists.get(e).contains(obs)) {
observerLists.get(e).add(obs);
}
public final void registerObserver(EventObserver obs) {
observers.add(obs);
}
protected void notifyObservers(Event e) {
if (observerLists.containsKey(e)) {
observerLists
.get(e)
.forEach(observer -> observer.onEvent(e));
}
observers.forEach(obs -> obs.onEvent(e));
}
public abstract void timePasses(Weekday day);

View File

@ -31,8 +31,8 @@ public class KingsHand extends EventEmitter implements EventObserver {
public KingsHand() {
}
public KingsHand(EventObserver obs, Event e) {
super(obs, e);
public KingsHand(EventObserver obs) {
super(obs);
}
@Override
@ -42,5 +42,6 @@ public class KingsHand extends EventEmitter implements EventObserver {
@Override
public void timePasses(Weekday day) {
// NOP
}
}

View File

@ -31,8 +31,8 @@ public class LordBaelish extends EventEmitter {
public LordBaelish() {
}
public LordBaelish(EventObserver obs, Event e) {
super(obs, e);
public LordBaelish(EventObserver obs) {
super(obs);
}
@Override

View File

@ -23,19 +23,16 @@
package com.iluwatar.event.aggregator;
import lombok.extern.slf4j.Slf4j;
/**
* LordVarys produces events.
*/
@Slf4j
public class LordVarys extends EventEmitter implements EventObserver {
public class LordVarys extends EventEmitter {
public LordVarys() {
}
public LordVarys(EventObserver obs, Event e) {
super(obs, e);
public LordVarys(EventObserver obs) {
super(obs);
}
@Override
@ -44,10 +41,4 @@ public class LordVarys extends EventEmitter implements EventObserver {
notifyObservers(Event.TRAITOR_DETECTED);
}
}
@Override
public void onEvent(Event e) {
notifyObservers(e);
}
}

View File

@ -31,8 +31,8 @@ public class Scout extends EventEmitter {
public Scout() {
}
public Scout(EventObserver obs, Event e) {
super(obs, e);
public Scout(EventObserver obs) {
super(obs);
}
@Override
@ -40,8 +40,5 @@ public class Scout extends EventEmitter {
if (day == Weekday.TUESDAY) {
notifyObservers(Event.WARSHIPS_APPROACHING);
}
if (day == Weekday.WEDNESDAY) {
notifyObservers(Event.WHITE_WALKERS_SIGHTED);
}
}
}

View File

@ -31,7 +31,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
@ -47,7 +46,7 @@ abstract class EventEmitterTest<E extends EventEmitter> {
/**
* Factory used to create a new instance of the test object with a default observer
*/
private final BiFunction<EventObserver, Event, E> factoryWithDefaultObserver;
private final Function<EventObserver, E> factoryWithDefaultObserver;
/**
* Factory used to create a new instance of the test object without passing a default observer
@ -68,7 +67,7 @@ abstract class EventEmitterTest<E extends EventEmitter> {
* Create a new event emitter test, using the given test object factories, special day and event
*/
EventEmitterTest(final Weekday specialDay, final Event event,
final BiFunction<EventObserver, Event, E> factoryWithDefaultObserver,
final Function<EventObserver, E> factoryWithDefaultObserver,
final Supplier<E> factoryWithoutDefaultObserver) {
this.specialDay = specialDay;
@ -130,8 +129,8 @@ abstract class EventEmitterTest<E extends EventEmitter> {
final var observer2 = mock(EventObserver.class);
final var emitter = this.factoryWithoutDefaultObserver.get();
emitter.registerObserver(observer1, event);
emitter.registerObserver(observer2, event);
emitter.registerObserver(observer1);
emitter.registerObserver(observer2);
testAllDays(specialDay, event, emitter, observer1, observer2);
}
@ -147,9 +146,9 @@ abstract class EventEmitterTest<E extends EventEmitter> {
final var observer1 = mock(EventObserver.class);
final var observer2 = mock(EventObserver.class);
final var emitter = this.factoryWithDefaultObserver.apply(defaultObserver, event);
emitter.registerObserver(observer1, event);
emitter.registerObserver(observer2, event);
final var emitter = this.factoryWithDefaultObserver.apply(defaultObserver);
emitter.registerObserver(observer1);
emitter.registerObserver(observer2);
testAllDays(specialDay, event, emitter, defaultObserver, observer1, observer2);
}

View File

@ -55,11 +55,7 @@ class KingsHandTest extends EventEmitterTest<KingsHand> {
@Test
void testPassThrough() throws Exception {
final var observer = mock(EventObserver.class);
final var kingsHand = new KingsHand();
kingsHand.registerObserver(observer, Event.STARK_SIGHTED);
kingsHand.registerObserver(observer, Event.WARSHIPS_APPROACHING);
kingsHand.registerObserver(observer, Event.TRAITOR_DETECTED);
kingsHand.registerObserver(observer, Event.WHITE_WALKERS_SIGHTED);
final var kingsHand = new KingsHand(observer);
// The kings hand should not pass any events before he received one
verifyZeroInteractions(observer);

View File

@ -34,9 +34,7 @@ class ScoutTest extends EventEmitterTest<Scout> {
* Create a new test instance, using the correct object factory
*/
public ScoutTest() {
super(Weekday.TUESDAY, Event.WARSHIPS_APPROACHING, Scout::new, Scout::new);
super(Weekday.TUESDAY, Event.WARSHIPS_APPROACHING, Scout::new, Scout::new);
}
}