diff --git a/composite-entity/README.md b/composite-entity/README.md new file mode 100644 index 000000000..4b4a3b9c3 --- /dev/null +++ b/composite-entity/README.md @@ -0,0 +1,122 @@ +--- +layout: pattern +title: Composite Entity +folder: composite-entity +permalink: /patterns/composite-entity/ +categories: Structural +tags: + - Enterprise Integration Pattern +--- + +## Intent + +It is used to model, represent, and manage a set of persistent objects that are interrelated, rather than representing them as individual fine-grained entities. + +## Explanation + +Real world example + +> For a console, there may be many interfaces that need to be managed and controlled. Using the composite entity pattern, dependent objects such as messages and signals can be combined together and controlled using a single object. + +In plain words + +> Composite entity pattern allows a set of related objects to be represented and managed by a unified object. + +**Programmatic Example** + +We need a generic solution for the problem. To achieve this, let's introduce a generic +Composite Entity Pattern. + +```java +public abstract class DependentObject { + + T data; + + public void setData(T message) { + this.data = message; + } + + public T getData() { + return data; + } +} + +public abstract class CoarseGrainedObject { + + DependentObject[] dependentObjects; + + public void setData(T... data) { + IntStream.range(0, data.length).forEach(i -> dependentObjects[i].setData(data[i])); + } + + public T[] getData() { + return (T[]) Arrays.stream(dependentObjects).map(DependentObject::getData).toArray(); + } +} + +``` + +The specialized composite entity `console` inherit from this base class as follows. + +```java +public class MessageDependentObject extends DependentObject { + +} + +public class SignalDependentObject extends DependentObject { + +} + +public class ConsoleCoarseGrainedObject extends CoarseGrainedObject { + + @Override + public String[] getData() { + super.getData(); + return new String[]{ + dependentObjects[0].getData(), dependentObjects[1].getData() + }; + } + + public void init() { + dependentObjects = new DependentObject[]{ + new MessageDependentObject(), new SignalDependentObject()}; + } +} + +public class CompositeEntity { + + private final ConsoleCoarseGrainedObject console = new ConsoleCoarseGrainedObject(); + + public void setData(String message, String signal) { + console.setData(message, signal); + } + + public String[] getData() { + return console.getData(); + } +} +``` + +Now managing the assignment of message and signal objects with the composite entity `console`. + +```java +var console = new CompositeEntity(); +console.init(); +console.setData("No Danger", "Green Light"); +Arrays.stream(console.getData()).forEach(LOGGER::info); +console.setData("Danger", "Red Light"); +Arrays.stream(console.getData()).forEach(LOGGER::info); +``` + +## Class diagram + +![alt text](./etc/composite_entity.urm.png "Composite Entity Pattern") + +## Applicability + +Use the Composite Entity Pattern in the following situation: + +* You want to manage multiple dependency objects through one object to adjust the degree of granularity between objects. At the same time, the lifetime of dependency objects depends on a coarse-grained object. +## Credits + +* [Composite Entity Pattern in wikipedia](https://en.wikipedia.org/wiki/Composite_entity_pattern) diff --git a/composite-entity/etc/composite-entity.urm.puml b/composite-entity/etc/composite-entity.urm.puml new file mode 100644 index 000000000..5ac4b9e65 --- /dev/null +++ b/composite-entity/etc/composite-entity.urm.puml @@ -0,0 +1,45 @@ +@startuml +package com.iluwatar.compositeentity { + class App { + + App(message: String, signal: String) + + main(args : String[]) {static} + } + class CompositeEntity{ + - console : ConsoleCoarseGrainedObject + + CompositeEntity() + + setData(message: String, signal: String) + + getData() + + init() + } + abstract CoarseGrainedObject{ + - dependentObjects : DependentObject[] + + CoarseGrainedObject() + + setData(data: T[]) + + getData() + } + abstract DependentObject{ + - data : T + + DependentObject() + + setData(data: T) + + getData() + } + class ConsoleCoarseGrainedObject{ + + ConsoleCoarseGrainedObject() + + getData() + + init() + } + class MessageDependentObject{ + + MessageDependentObject() + } + class SignalDependentObject{ + + SignalDependentObject() + } + + MessageDependentObject --|> DependentObject + SignalDependentObject --|> DependentObject + ConsoleCoarseGrainedObject --|> CoarseGrainedObject + CompositeEntity -right-> ConsoleCoarseGrainedObject + CoarseGrainedObject "1" o--> "0.." DependentObject + App .right.> CompositeEntity +} +@enduml diff --git a/composite-entity/etc/composite_entity.urm.png b/composite-entity/etc/composite_entity.urm.png new file mode 100644 index 000000000..d6c29a718 Binary files /dev/null and b/composite-entity/etc/composite_entity.urm.png differ diff --git a/composite-entity/pom.xml b/composite-entity/pom.xml new file mode 100644 index 000000000..fd05950a4 --- /dev/null +++ b/composite-entity/pom.xml @@ -0,0 +1,39 @@ + + + + java-design-patterns + com.iluwatar + 1.25.0-SNAPSHOT + + 4.0.0 + composite-entity + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.composite-entity.com.iluwatar.compositeentity.App + + + + + + + + + + \ No newline at end of file diff --git a/composite-entity/src/main/java/com/iluwatar/compositeentity/App.java b/composite-entity/src/main/java/com/iluwatar/compositeentity/App.java new file mode 100644 index 000000000..241bc5ce6 --- /dev/null +++ b/composite-entity/src/main/java/com/iluwatar/compositeentity/App.java @@ -0,0 +1,38 @@ +package com.iluwatar.compositeentity; + +import java.util.Arrays; +import lombok.extern.slf4j.Slf4j; + + +/** + * Composite entity is a Java EE Software design pattern and it is used to model, represent, and + * manage a set of interrelated persistent objects rather than representing them as individual + * fine-grained entity beans, and also a composite entity bean represents a graph of objects. + */ +@Slf4j +public class App { + + + /** + * An instance that a console manages two related objects. + */ + public App(String message, String signal) { + var console = new CompositeEntity(); + console.init(); + console.setData(message, signal); + Arrays.stream(console.getData()).forEach(LOGGER::info); + console.setData("Danger", "Red Light"); + Arrays.stream(console.getData()).forEach(LOGGER::info); + } + + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { + + new App("No Danger", "Green Light"); + + } +} diff --git a/composite-entity/src/main/java/com/iluwatar/compositeentity/CoarseGrainedObject.java b/composite-entity/src/main/java/com/iluwatar/compositeentity/CoarseGrainedObject.java new file mode 100644 index 000000000..45e5d7959 --- /dev/null +++ b/composite-entity/src/main/java/com/iluwatar/compositeentity/CoarseGrainedObject.java @@ -0,0 +1,23 @@ +package com.iluwatar.compositeentity; + +import java.util.Arrays; +import java.util.stream.IntStream; + +/** + * A coarse-grained object is an object with its own life cycle manages its own relationships to + * other objects. It can be an object contained in the composite entity, or, composite entity itself + * can be the coarse-grained object which holds dependent objects. + */ + +public abstract class CoarseGrainedObject { + + DependentObject[] dependentObjects; + + public void setData(T... data) { + IntStream.range(0, data.length).forEach(i -> dependentObjects[i].setData(data[i])); + } + + public T[] getData() { + return (T[]) Arrays.stream(dependentObjects).map(DependentObject::getData).toArray(); + } +} diff --git a/composite-entity/src/main/java/com/iluwatar/compositeentity/CompositeEntity.java b/composite-entity/src/main/java/com/iluwatar/compositeentity/CompositeEntity.java new file mode 100644 index 000000000..70f067740 --- /dev/null +++ b/composite-entity/src/main/java/com/iluwatar/compositeentity/CompositeEntity.java @@ -0,0 +1,23 @@ +package com.iluwatar.compositeentity; + +/** + * Composite entity is the coarse-grained entity bean which may be the coarse-grained object, or may + * contain a reference to the coarse-grained object. + */ + +public class CompositeEntity { + + private final ConsoleCoarseGrainedObject console = new ConsoleCoarseGrainedObject(); + + public void setData(String message, String signal) { + console.setData(message, signal); + } + + public String[] getData() { + return console.getData(); + } + + public void init() { + console.init(); + } +} \ No newline at end of file diff --git a/composite-entity/src/main/java/com/iluwatar/compositeentity/ConsoleCoarseGrainedObject.java b/composite-entity/src/main/java/com/iluwatar/compositeentity/ConsoleCoarseGrainedObject.java new file mode 100644 index 000000000..635dd98ca --- /dev/null +++ b/composite-entity/src/main/java/com/iluwatar/compositeentity/ConsoleCoarseGrainedObject.java @@ -0,0 +1,20 @@ +package com.iluwatar.compositeentity; + +/** + * A specific CoarseGrainedObject to implement a console. + */ + +public class ConsoleCoarseGrainedObject extends CoarseGrainedObject { + + @Override + public String[] getData() { + return new String[]{ + dependentObjects[0].getData(), dependentObjects[1].getData() + }; + } + + public void init() { + dependentObjects = new DependentObject[]{ + new MessageDependentObject(), new SignalDependentObject()}; + } +} \ No newline at end of file diff --git a/composite-entity/src/main/java/com/iluwatar/compositeentity/DependentObject.java b/composite-entity/src/main/java/com/iluwatar/compositeentity/DependentObject.java new file mode 100644 index 000000000..ede4f473a --- /dev/null +++ b/composite-entity/src/main/java/com/iluwatar/compositeentity/DependentObject.java @@ -0,0 +1,20 @@ +package com.iluwatar.compositeentity; + +/** + * It is an object, which can contain other dependent objects (there may be a tree of objects within + * the composite entity), that depends on the coarse-grained object and has its life cycle managed + * by the coarse-grained object. + */ + +public abstract class DependentObject { + + T data; + + public void setData(T message) { + this.data = message; + } + + public T getData() { + return data; + } +} diff --git a/composite-entity/src/main/java/com/iluwatar/compositeentity/MessageDependentObject.java b/composite-entity/src/main/java/com/iluwatar/compositeentity/MessageDependentObject.java new file mode 100644 index 000000000..51e7c04df --- /dev/null +++ b/composite-entity/src/main/java/com/iluwatar/compositeentity/MessageDependentObject.java @@ -0,0 +1,9 @@ +package com.iluwatar.compositeentity; + +/** + * The first DependentObject to show message. + */ + +public class MessageDependentObject extends DependentObject { + +} \ No newline at end of file diff --git a/composite-entity/src/main/java/com/iluwatar/compositeentity/SignalDependentObject.java b/composite-entity/src/main/java/com/iluwatar/compositeentity/SignalDependentObject.java new file mode 100644 index 000000000..ff6691c29 --- /dev/null +++ b/composite-entity/src/main/java/com/iluwatar/compositeentity/SignalDependentObject.java @@ -0,0 +1,9 @@ +package com.iluwatar.compositeentity; + +/** + * The second DependentObject to show message. + */ + +public class SignalDependentObject extends DependentObject { + +} \ No newline at end of file diff --git a/composite-entity/src/test/java/com/iluwatar/compositeentity/AppTest.java b/composite-entity/src/test/java/com/iluwatar/compositeentity/AppTest.java new file mode 100644 index 000000000..5a84d8de3 --- /dev/null +++ b/composite-entity/src/test/java/com/iluwatar/compositeentity/AppTest.java @@ -0,0 +1,25 @@ +package com.iluwatar.compositeentity; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.Test; + +/** + * com.iluwatar.compositeentity.App running test + */ +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + *

+ * Solution: Inserted assertion to check whether the execution of the main method in {@link + * App#main(String[])} throws an exception. + */ + + @Test + void shouldExecuteApplicationWithoutException() { + + assertDoesNotThrow(() -> App.main(new String[]{})); + + } +} diff --git a/composite-entity/src/test/java/com/iluwatar/compositeentity/PersistenceTest.java b/composite-entity/src/test/java/com/iluwatar/compositeentity/PersistenceTest.java new file mode 100644 index 000000000..010a99ebd --- /dev/null +++ b/composite-entity/src/test/java/com/iluwatar/compositeentity/PersistenceTest.java @@ -0,0 +1,32 @@ +package com.iluwatar.compositeentity; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class PersistenceTest { + + final static ConsoleCoarseGrainedObject console = new ConsoleCoarseGrainedObject(); + + @Test + void dependentObjectChangedForPersistenceTest() { + MessageDependentObject dependentObject = new MessageDependentObject(); + console.init(); + console.dependentObjects[0] = dependentObject; + String message = "Danger"; + assertNull(console.dependentObjects[0].getData()); + dependentObject.setData(message); + assertEquals(message, console.dependentObjects[0].getData()); + } + + @Test + void coarseGrainedObjectChangedForPersistenceTest() { + MessageDependentObject dependentObject = new MessageDependentObject(); + console.init(); + console.dependentObjects[0] = dependentObject; + String message = "Danger"; + assertNull(console.dependentObjects[0].getData()); + console.setData(message); + assertEquals(message, dependentObject.getData()); + } +} diff --git a/pom.xml b/pom.xml index 4aac0a2a8..23e9dbb4d 100644 --- a/pom.xml +++ b/pom.xml @@ -224,6 +224,7 @@ parameter-object active-object model-view-viewmodel + composite-entity