Compare commits

..

1 Commits

Author SHA1 Message Date
d2fab302ce Refactor and add explanation for value object 2022-01-04 20:36:57 +02:00
7 changed files with 99 additions and 188 deletions

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

@ -17,19 +17,19 @@ and to interleave the execution of functions without hard coding them together.
## Explanation
Recursion is a frequently adopted technique for solving algorithmic problems in a divide and conquer
style. For example, calculating Fibonacci accumulating sum and factorials. In these kinds of
problems, recursion is more straightforward than its loop counterpart. Furthermore, recursion may
need less code and looks more concise. There is a saying that every recursion problem can be solved
using a loop with the cost of writing code that is more difficult to understand.
style. For example calculating fibonacci accumulating sum and factorials. In these kinds of problems
recursion is more straightforward than their loop counterpart. Furthermore recursion may need less
code and looks more concise. There is a saying that every recursion problem can be solved using
a loop with the cost of writing code that is more difficult to understand.
However, recursion-type solutions have one big caveat. For each recursive call, it typically needs
However recursion type solutions have one big caveat. For each recursive call it typically needs
an intermediate value stored and there is a limited amount of stack memory available. Running out of
stack memory creates a stack overflow error and halts the program execution.
Trampoline pattern is a trick that allows defining recursive algorithms in Java without blowing the
Trampoline pattern is a trick that allows us define recursive algorithms in Java without blowing the
stack.
Real-world example
Real world example
> A recursive Fibonacci calculation without the stack overflow problem using the Trampoline pattern.
@ -105,26 +105,24 @@ public interface Trampoline<T> {
Using the `Trampoline` to get Fibonacci values.
```java
public static void main(String[] args) {
LOGGER.info("Start calculating war casualties");
var result = loop(10, 1).result();
LOGGER.info("The number of orcs perished in the war: {}", result);
}
public static Trampoline<Integer> loop(int times, int prod) {
public static Trampoline<Integer> loop(int times, int prod) {
if (times == 0) {
return Trampoline.done(prod);
return Trampoline.done(prod);
} else {
return Trampoline.more(() -> loop(times - 1, prod * times));
return Trampoline.more(() -> loop(times - 1, prod * times));
}
}
}
log.info("start pattern");
var result = loop(10, 1).result();
log.info("result {}", result);
```
Program output:
```
19:22:24.462 [main] INFO com.iluwatar.trampoline.TrampolineApp - Start calculating war casualties
19:22:24.472 [main] INFO com.iluwatar.trampoline.TrampolineApp - The number of orcs perished in the war: 3628800
start pattern
result 3628800
```
## Class diagram
@ -135,8 +133,8 @@ Program output:
Use the Trampoline pattern when
* For implementing tail-recursive functions. This pattern allows to switch on a stackless operation.
* For interleaving execution of two or more functions on the same thread.
* For implementing tail recursive function. This pattern allows to switch on a stackless operation.
* For interleaving the execution of two or more functions on the same thread.
## Known uses

View File

@ -107,4 +107,6 @@ public interface Trampoline<T> {
}
};
}
}

View File

@ -39,9 +39,9 @@ public class TrampolineApp {
* Main program for showing pattern. It does loop with factorial function.
*/
public static void main(String[] args) {
LOGGER.info("Start calculating war casualties");
LOGGER.info("start pattern");
var result = loop(10, 1).result();
LOGGER.info("The number of orcs perished in the war: {}", result);
LOGGER.info("result {}", result);
}
@ -55,4 +55,5 @@ public class TrampolineApp {
return Trampoline.more(() -> loop(times - 1, prod * times));
}
}
}

View File

@ -10,19 +10,80 @@ tags:
---
## Intent
Provide objects which follow value semantics rather than reference semantics.
This means value objects' equality are not based on identity. Two value objects are
This means value objects' equality is not based on identity. Two value objects are
equal when they have the same value, not necessarily being the same object.
## Explanation
Real-world example
> There is a class for hero statistics in a role-playing game. The statistics contain attributes
> such as strength, intelligence, and luck. The statistics of different heroes should be equal
> when all the attributes are equal.
In plain words
> Value objects are equal when their attributes have the same value
Wikipedia says
> In computer science, a value object is a small object that represents a simple entity whose
> equality is not based on identity: i.e. two value objects are equal when they have the same
> value, not necessarily being the same object.
**Programmatic Example**
Here is the `HeroStat` class that is the value object. Notice the use of
[Lombok's `@Value`](https://projectlombok.org/features/Value) annotation.
```java
@Value(staticConstructor = "valueOf")
class HeroStat {
int strength;
int intelligence;
int luck;
}
```
The example creates three different `HeroStat`s and compares their equality.
```java
var statA = HeroStat.valueOf(10, 5, 0);
var statB = HeroStat.valueOf(10, 5, 0);
var statC = HeroStat.valueOf(5, 1, 8);
LOGGER.info(statA.toString());
LOGGER.info(statB.toString());
LOGGER.info(statC.toString());
LOGGER.info("Is statA and statB equal : {}", statA.equals(statB));
LOGGER.info("Is statA and statC equal : {}", statA.equals(statC));
```
Here's the console output.
```
20:11:12.199 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=10, intelligence=5, luck=0)
20:11:12.202 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=10, intelligence=5, luck=0)
20:11:12.202 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=5, intelligence=1, luck=8)
20:11:12.202 [main] INFO com.iluwatar.value.object.App - Is statA and statB equal : true
20:11:12.203 [main] INFO com.iluwatar.value.object.App - Is statA and statC equal : false
```
## Class diagram
![alt text](./etc/value-object.png "Value Object")
## Applicability
Use the Value Object when
* You need to measure the objects' equality based on the objects' value
* The object's equality needs to be based on the object's value
## Real world examples
## Known uses
* [java.util.Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html)
* [java.time.LocalDate](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html)
@ -31,6 +92,7 @@ Use the Value Object when
## Credits
* [Patterns of Enterprise Application Architecture](http://www.martinfowler.com/books/eaa.html)
* [ValueObject](https://martinfowler.com/bliki/ValueObject.html)
* [VALJOs - Value Java Objects : Stephen Colebourne's blog](http://blog.joda.org/2014/03/valjos-value-java-objects.html)
* [Value Object : Wikipedia](https://en.wikipedia.org/wiki/Value_object)
* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=f27d2644fbe5026ea448791a8ad09c94)

View File

@ -43,7 +43,7 @@ import lombok.extern.slf4j.Slf4j;
public class App {
/**
* This practice creates three HeroStats(Value object) and checks equality between those.
* This example creates three HeroStats (value objects) and checks equality between those.
*/
public static void main(String[] args) {
var statA = HeroStat.valueOf(10, 5, 0);
@ -51,6 +51,8 @@ public class App {
var statC = HeroStat.valueOf(5, 1, 8);
LOGGER.info(statA.toString());
LOGGER.info(statB.toString());
LOGGER.info(statC.toString());
LOGGER.info("Is statA and statB equal : {}", statA.equals(statB));
LOGGER.info("Is statA and statC equal : {}", statA.equals(statC));

View File

@ -23,10 +23,7 @@
package com.iluwatar.value.object;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.Value;
/**
* HeroStat is a value object.
@ -35,23 +32,10 @@ import lombok.ToString;
* http://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html
* </a>
*/
@Getter
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor
public class HeroStat {
// Stats for a hero
private final int strength;
private final int intelligence;
private final int luck;
// Static factory method to create new instances.
public static HeroStat valueOf(int strength, int intelligence, int luck) {
return new HeroStat(strength, intelligence, luck);
}
// The clone() method should not be public. Just don't override it.
@Value(staticConstructor = "valueOf")
class HeroStat {
int strength;
int intelligence;
int luck;
}