entry : documentProperties.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+
+ // Append key-value pair
+ builder.append("[").append(key).append(" : ").append(value).append("]");
+
+ // Add comma if not last property
+ if (++currentPropertyIndex < numProperties) {
+ builder.append(", ");
+ }
+ }
+
+ builder.append("]");
+ return builder.toString();
+ }
+}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java
new file mode 100644
index 000000000000..607b4a7f7913
--- /dev/null
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java
@@ -0,0 +1,88 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument;
+
+import com.iluwatar.abstractdocument.domain.Car;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.List;
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * The Abstract Document pattern enables handling additional, non-static properties. This pattern
+ * uses concept of traits to enable type safety and separate properties of different classes into
+ * set of interfaces.
+ *
+ * In Abstract Document pattern,({@link AbstractDocument}) fully implements {@link Document})
+ * interface. Traits are then defined to enable access to properties in usual, static way.
+ */
+@Slf4j
+public class App {
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+ LOGGER.info("Constructing parts and car");
+
+ var wheelProperties =
+ Map.of(
+ Property.TYPE.toString(), "wheel",
+ Property.MODEL.toString(), "15C",
+ Property.PRICE.toString(), 100L);
+
+ var doorProperties =
+ Map.of(
+ Property.TYPE.toString(), "door",
+ Property.MODEL.toString(), "Lambo",
+ Property.PRICE.toString(), 300L);
+
+ var carProperties =
+ Map.of(
+ Property.MODEL.toString(),
+ "300SL",
+ Property.PRICE.toString(),
+ 10000L,
+ Property.PARTS.toString(),
+ List.of(wheelProperties, doorProperties));
+
+ var car = new Car(carProperties);
+
+ LOGGER.info("Here is our car:");
+ LOGGER.info("-> model: {}", car.getModel().orElseThrow());
+ LOGGER.info("-> price: {}", car.getPrice().orElseThrow());
+ LOGGER.info("-> parts: ");
+ car.getParts()
+ .forEach(
+ p ->
+ LOGGER.info(
+ "\t{}/{}/{}",
+ p.getType().orElse(null),
+ p.getModel().orElse(null),
+ p.getPrice().orElse(null)));
+ }
+}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java
new file mode 100644
index 000000000000..79a51b610337
--- /dev/null
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java
@@ -0,0 +1,59 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument;
+
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+/** Document interface. */
+public interface Document {
+
+ /**
+ * Puts the value related to the key.
+ *
+ * @param key element key
+ * @param value element value
+ * @return Void
+ */
+ Void put(String key, Object value);
+
+ /**
+ * Gets the value for the key.
+ *
+ * @param key element key
+ * @return value or null
+ */
+ Object get(String key);
+
+ /**
+ * Gets the stream of child documents.
+ *
+ * @param key element key
+ * @param constructor constructor of child class
+ * @return child documents
+ */
+ Stream children(String key, Function, T> constructor);
+}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java
new file mode 100644
index 000000000000..93fbbb9c1eae
--- /dev/null
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java
@@ -0,0 +1,36 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument.domain;
+
+import com.iluwatar.abstractdocument.AbstractDocument;
+import java.util.Map;
+
+/** Car entity. */
+public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
+
+ public Car(Map properties) {
+ super(properties);
+ }
+}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java
new file mode 100644
index 000000000000..6f517588e5a0
--- /dev/null
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument.domain;
+
+import com.iluwatar.abstractdocument.Document;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.Optional;
+
+/** HasModel trait for static access to 'model' property. */
+public interface HasModel extends Document {
+
+ default Optional getModel() {
+ return Optional.ofNullable((String) get(Property.MODEL.toString()));
+ }
+}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java
new file mode 100644
index 000000000000..8bffa753e6ef
--- /dev/null
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument.domain;
+
+import com.iluwatar.abstractdocument.Document;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.stream.Stream;
+
+/** HasParts trait for static access to 'parts' property. */
+public interface HasParts extends Document {
+
+ default Stream getParts() {
+ return children(Property.PARTS.toString(), Part::new);
+ }
+}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java
new file mode 100644
index 000000000000..ce876e5faf54
--- /dev/null
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument.domain;
+
+import com.iluwatar.abstractdocument.Document;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.Optional;
+
+/** HasPrice trait for static access to 'price' property. */
+public interface HasPrice extends Document {
+
+ default Optional getPrice() {
+ return Optional.ofNullable((Number) get(Property.PRICE.toString()));
+ }
+}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java
new file mode 100644
index 000000000000..5e0f49df7b7b
--- /dev/null
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument.domain;
+
+import com.iluwatar.abstractdocument.Document;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.Optional;
+
+/** HasType trait for static access to 'type' property. */
+public interface HasType extends Document {
+
+ default Optional getType() {
+ return Optional.ofNullable((String) get(Property.TYPE.toString()));
+ }
+}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java
new file mode 100644
index 000000000000..6eec08b0d2a4
--- /dev/null
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java
@@ -0,0 +1,36 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument.domain;
+
+import com.iluwatar.abstractdocument.AbstractDocument;
+import java.util.Map;
+
+/** Part entity. */
+public class Part extends AbstractDocument implements HasType, HasModel, HasPrice {
+
+ public Part(Map properties) {
+ super(properties);
+ }
+}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/enums/Property.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/enums/Property.java
new file mode 100644
index 000000000000..3e0d6d10ab1f
--- /dev/null
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/enums/Property.java
@@ -0,0 +1,33 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument.domain.enums;
+
+/** Enum To Describe Property type. */
+public enum Property {
+ PARTS,
+ TYPE,
+ PRICE,
+ MODEL
+}
diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java
new file mode 100644
index 000000000000..a098517c3a69
--- /dev/null
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java
@@ -0,0 +1,130 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+/** AbstractDocument test class */
+class AbstractDocumentTest {
+
+ private static final String KEY = "key";
+ private static final String VALUE = "value";
+
+ private static class DocumentImplementation extends AbstractDocument {
+
+ DocumentImplementation(Map properties) {
+ super(properties);
+ }
+ }
+
+ private final DocumentImplementation document = new DocumentImplementation(new HashMap<>());
+
+ @Test
+ void shouldPutAndGetValue() {
+ document.put(KEY, VALUE);
+ assertEquals(VALUE, document.get(KEY));
+ }
+
+ @Test
+ void shouldRetrieveChildren() {
+ var children = List.of(Map.of(), Map.of());
+
+ document.put(KEY, children);
+
+ var childrenStream = document.children(KEY, DocumentImplementation::new);
+ assertNotNull(children);
+ assertEquals(2, childrenStream.count());
+ }
+
+ @Test
+ void shouldRetrieveEmptyStreamForNonExistingChildren() {
+ var children = document.children(KEY, DocumentImplementation::new);
+ assertNotNull(children);
+ assertEquals(0, children.count());
+ }
+
+ @Test
+ void shouldIncludePropsInToString() {
+ var props = Map.of(KEY, (Object) VALUE);
+ var document = new DocumentImplementation(props);
+ assertTrue(document.toString().contains(KEY));
+ assertTrue(document.toString().contains(VALUE));
+ }
+
+ @Test
+ void shouldHandleExceptionDuringConstruction() {
+ Map invalidProperties =
+ null; // Invalid properties, causing NullPointerException
+
+ // Throw null pointer exception
+ assertThrows(
+ NullPointerException.class,
+ () -> {
+ // Attempt to construct a document with invalid properties
+ new DocumentImplementation(invalidProperties);
+ });
+ }
+
+ @Test
+ void shouldPutAndGetNestedDocument() {
+ // Creating a nested document
+ DocumentImplementation nestedDocument = new DocumentImplementation(new HashMap<>());
+ nestedDocument.put("nestedKey", "nestedValue");
+
+ document.put("nested", nestedDocument);
+
+ // Retrieving the nested document
+ DocumentImplementation retrievedNestedDocument =
+ (DocumentImplementation) document.get("nested");
+
+ assertNotNull(retrievedNestedDocument);
+ assertEquals("nestedValue", retrievedNestedDocument.get("nestedKey"));
+ }
+
+ @Test
+ void shouldUpdateExistingValue() {
+ // Arrange
+ final String key = "key";
+ final String originalValue = "originalValue";
+ final String updatedValue = "updatedValue";
+
+ // Initializing the value
+ document.put(key, originalValue);
+
+ // Verifying that the initial value is retrieved correctly
+ assertEquals(originalValue, document.get(key));
+
+ // Updating the value
+ document.put(key, updatedValue);
+
+ // Verifying that the updated value is retrieved correctly
+ assertEquals(updatedValue, document.get(key));
+ }
+}
diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java
new file mode 100644
index 000000000000..16dcba0db37f
--- /dev/null
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java
@@ -0,0 +1,44 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Simple App 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} throws an exception.
+ */
+ @Test
+ void shouldExecuteAppWithoutException() {
+ assertDoesNotThrow(() -> App.main(null));
+ }
+}
diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
new file mode 100644
index 000000000000..fc29dea45c43
--- /dev/null
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
@@ -0,0 +1,71 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractdocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.iluwatar.abstractdocument.domain.Car;
+import com.iluwatar.abstractdocument.domain.Part;
+import com.iluwatar.abstractdocument.domain.enums.Property;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+/** Test for Part and Car */
+class DomainTest {
+
+ private static final String TEST_PART_TYPE = "test-part-type";
+ private static final String TEST_PART_MODEL = "test-part-model";
+ private static final long TEST_PART_PRICE = 0L;
+
+ private static final String TEST_CAR_MODEL = "test-car-model";
+ private static final long TEST_CAR_PRICE = 1L;
+
+ @Test
+ void shouldConstructPart() {
+ var partProperties =
+ Map.of(
+ Property.TYPE.toString(), TEST_PART_TYPE,
+ Property.MODEL.toString(), TEST_PART_MODEL,
+ Property.PRICE.toString(), (Object) TEST_PART_PRICE);
+ var part = new Part(partProperties);
+ assertEquals(TEST_PART_TYPE, part.getType().orElseThrow());
+ assertEquals(TEST_PART_MODEL, part.getModel().orElseThrow());
+ assertEquals(TEST_PART_PRICE, part.getPrice().orElseThrow());
+ }
+
+ @Test
+ void shouldConstructCar() {
+ var carProperties =
+ Map.of(
+ Property.MODEL.toString(), TEST_CAR_MODEL,
+ Property.PRICE.toString(), TEST_CAR_PRICE,
+ Property.PARTS.toString(), List.of(Map.of(), Map.of()));
+ var car = new Car(carProperties);
+ assertEquals(TEST_CAR_MODEL, car.getModel().orElseThrow());
+ assertEquals(TEST_CAR_PRICE, car.getPrice().orElseThrow());
+ assertEquals(2, car.getParts().count());
+ }
+}
diff --git a/abstract-factory/README.md b/abstract-factory/README.md
new file mode 100644
index 000000000000..ee9823a39a5f
--- /dev/null
+++ b/abstract-factory/README.md
@@ -0,0 +1,227 @@
+---
+title: "Abstract Factory Pattern in Java: Mastering Object Creation Elegantly"
+shortTitle: Abstract Factory
+description: "Learn the Abstract Factory pattern in Java with real-world examples, class diagrams, and tutorials. Understand its intent, applicability, benefits, and known uses to enhance your design pattern knowledge."
+category: Creational
+language: en
+tag:
+ - Abstraction
+ - Decoupling
+ - Gang of Four
+ - Instantiation
+ - Polymorphism
+---
+
+## Also known as
+
+* Kit
+
+## Intent of Abstract Factory Design Pattern
+
+The Abstract Factory pattern in Java provides an interface for creating families of related or dependent objects without specifying their concrete classes, enhancing modularity and flexibility in software design.
+
+## Detailed Explanation of Abstract Factory Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a furniture company that uses the Abstract Factory pattern in Java to produce various styles of furniture: modern, Victorian, and rustic. Each style includes products like chairs, tables, and sofas. To ensure consistency within each style, the company uses an Abstract Factory pattern.
+>
+> In this scenario, the Abstract Factory is an interface for creating families of related furniture objects (chairs, tables, sofas). Each concrete factory (ModernFurnitureFactory, VictorianFurnitureFactory, RusticFurnitureFactory) implements the Abstract Factory interface and creates a set of products that match the specific style. This way, clients can create a whole set of modern or Victorian furniture without worrying about the details of their instantiation. This maintains a consistent style and allows easy swapping of one style of furniture for another.
+
+In plain words
+
+> A factory of factories; a factory that groups the individual but related/dependent factories together without specifying their concrete classes.
+
+Wikipedia says
+
+> The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes
+
+Class diagram
+
+
+
+## Programmatic Example of Abstract Factory in Java
+
+To create a kingdom using the Abstract Factory pattern in Java, we need objects with a common theme. The elven kingdom needs an elven king, elven castle, and elven army whereas the orcish kingdom needs an orcish king, orcish castle, and orcish army. There is a dependency between the objects in the kingdom.
+
+Translating the kingdom example above. First of all, we have some interfaces and implementation for the objects in the kingdom.
+
+```java
+public interface Castle {
+ String getDescription();
+}
+
+public interface King {
+ String getDescription();
+}
+
+public interface Army {
+ String getDescription();
+}
+
+// Elven implementations ->
+public class ElfCastle implements Castle {
+ static final String DESCRIPTION = "This is the elven castle!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
+
+public class ElfKing implements King {
+ static final String DESCRIPTION = "This is the elven king!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
+
+public class ElfArmy implements Army {
+ static final String DESCRIPTION = "This is the elven Army!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
+
+// Orcish implementations similarly -> ...
+```
+
+Then we have the abstraction and implementations for the kingdom factory.
+
+```java
+public interface KingdomFactory {
+ Castle createCastle();
+
+ King createKing();
+
+ Army createArmy();
+}
+
+public class ElfKingdomFactory implements KingdomFactory {
+
+ @Override
+ public Castle createCastle() {
+ return new ElfCastle();
+ }
+
+ @Override
+ public King createKing() {
+ return new ElfKing();
+ }
+
+ @Override
+ public Army createArmy() {
+ return new ElfArmy();
+ }
+}
+
+// Orcish implementations similarly -> ...
+```
+
+Now, we can design a factory for our different kingdom factories. In this example, we created `FactoryMaker`, responsible for returning an instance of either `ElfKingdomFactory` or `OrcKingdomFactory`. The client can use `FactoryMaker` to create the desired concrete factory which, in turn, will produce different concrete objects (derived from `Army`, `King`, `Castle`). In this example, we also used an enum to parameterize which type of kingdom factory the client will ask for.
+
+```java
+public static class FactoryMaker {
+
+ public enum KingdomType {
+ ELF, ORC
+ }
+
+ public static KingdomFactory makeFactory(KingdomType type) {
+ return switch (type) {
+ case ELF -> new ElfKingdomFactory();
+ case ORC -> new OrcKingdomFactory();
+ };
+ }
+}
+```
+
+Here is the main function of our example application:
+
+```java
+LOGGER.info("elf kingdom");
+createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+LOGGER.info(kingdom.getArmy().getDescription());
+LOGGER.info(kingdom.getCastle().getDescription());
+LOGGER.info(kingdom.getKing().getDescription());
+
+LOGGER.info("orc kingdom");
+createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+LOGGER.info(kingdom.getArmy().getDescription());
+LOGGER.info(kingdom.getCastle().getDescription());
+LOGGER.info(kingdom.getKing().getDescription());
+```
+
+The program output:
+
+```
+07:35:46.340 [main] INFO com.iluwatar.abstractfactory.App -- elf kingdom
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven army!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven castle!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven king!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- orc kingdom
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc army!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc castle!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc king!
+```
+
+## When to Use the Abstract Factory Pattern in Java
+
+Use the Abstract Factory pattern in Java when:
+
+* The system should be independent of how its products are created, composed, and represented.
+* You need to configure the system with one of multiple families of products.
+* A family of related product objects must be used together, enforcing consistency.
+* You want to provide a class library of products, exposing only their interfaces, not their implementations.
+* The lifetime of dependencies is shorter than the consumer's lifetime.
+* Dependencies need to be constructed using runtime values or parameters.
+* You need to choose which product to use from a family at runtime.
+* Adding new products or families should not require changes to existing code.
+
+## Abstract Factory Pattern Java Tutorials
+
+* [Abstract Factory Design Pattern in Java (DigitalOcean)](https://www.digitalocean.com/community/tutorials/abstract-factory-design-pattern-in-java)
+* [Abstract Factory(Refactoring Guru)](https://refactoring.guru/design-patterns/abstract-factory)
+
+## Benefits and Trade-offs of Abstract Factory Pattern
+
+Benefits:
+
+* Flexibility: Easily switch between product families without code modifications.
+
+* Decoupling: Client code only interacts with abstract interfaces, promoting portability and maintainability.
+
+* Reusability: Abstract factories and products facilitate component reuse across projects.
+
+* Maintainability: Changes to individual product families are localized, simplifying updates.
+
+Trade-offs:
+
+* Complexity: Defining abstract interfaces and concrete factories adds initial overhead.
+
+* Indirectness: Client code interacts with products indirectly through factories, potentially reducing transparency.
+
+## Real-World Applications of Abstract Factory Pattern in Java
+
+* Java Swing's `LookAndFeel` classes for providing different look-and-feel options.
+* Various implementations in the Java Abstract Window Toolkit (AWT) for creating different GUI components.
+* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
+* [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--)
+* [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--)
+
+## Related Java Design Patterns
+
+* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): Abstract Factory uses Factory Methods to create products.
+* [Singleton](https://java-design-patterns.com/patterns/singleton/): Abstract Factory classes are often implemented as Singletons.
+* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/): Similar to Abstract Factory but focuses on configuring and managing a set of related objects in a flexible way.
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Design Patterns in Java](https://amzn.to/3Syw0vC)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3HWNf4U)
diff --git a/abstract-factory/etc/abstract-factory.png b/abstract-factory/etc/abstract-factory.png
deleted file mode 100644
index c709276b662b..000000000000
Binary files a/abstract-factory/etc/abstract-factory.png and /dev/null differ
diff --git a/abstract-factory/etc/abstract-factory.ucls b/abstract-factory/etc/abstract-factory.ucls
deleted file mode 100644
index bdf1e1510773..000000000000
--- a/abstract-factory/etc/abstract-factory.ucls
+++ /dev/null
@@ -1,159 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/abstract-factory/etc/abstract-factory.urm.png b/abstract-factory/etc/abstract-factory.urm.png
new file mode 100644
index 000000000000..836858a2c652
Binary files /dev/null and b/abstract-factory/etc/abstract-factory.urm.png differ
diff --git a/abstract-factory/etc/abstract-factory.urm.puml b/abstract-factory/etc/abstract-factory.urm.puml
new file mode 100644
index 000000000000..999091ef54f6
--- /dev/null
+++ b/abstract-factory/etc/abstract-factory.urm.puml
@@ -0,0 +1,101 @@
+@startuml
+package com.iluwatar.abstractfactory {
+ class App {
+ - LOGGER : Logger {static}
+ - army : Army
+ - castle : Castle
+ - king : King
+ + App()
+ + createKingdom(factory : KingdomFactory)
+ + getArmy() : Army
+ ~ getArmy(factory : KingdomFactory) : Army
+ + getCastle() : Castle
+ ~ getCastle(factory : KingdomFactory) : Castle
+ + getKing() : King
+ ~ getKing(factory : KingdomFactory) : King
+ + main(args : String[]) {static}
+ - setArmy(army : Army)
+ - setCastle(castle : Castle)
+ - setKing(king : King)
+ }
+ class FactoryMaker {
+ + FactoryMaker()
+ + makeFactory(type : KingdomType) : KingdomFactory {static}
+ }
+ enum KingdomType {
+ + ELF {static}
+ + ORC {static}
+ + valueOf(name : String) : KingdomType {static}
+ + values() : KingdomType[] {static}
+ }
+ interface Army {
+ + getDescription() : String {abstract}
+ }
+ interface Castle {
+ + getDescription() : String {abstract}
+ }
+ class ElfArmy {
+ ~ DESCRIPTION : String {static}
+ + ElfArmy()
+ + getDescription() : String
+ }
+ class ElfCastle {
+ ~ DESCRIPTION : String {static}
+ + ElfCastle()
+ + getDescription() : String
+ }
+ class ElfKing {
+ ~ DESCRIPTION : String {static}
+ + ElfKing()
+ + getDescription() : String
+ }
+ class ElfKingdomFactory {
+ + ElfKingdomFactory()
+ + createArmy() : Army
+ + createCastle() : Castle
+ + createKing() : King
+ }
+ interface King {
+ + getDescription() : String {abstract}
+ }
+ interface KingdomFactory {
+ + createArmy() : Army {abstract}
+ + createCastle() : Castle {abstract}
+ + createKing() : King {abstract}
+ }
+ class OrcArmy {
+ ~ DESCRIPTION : String {static}
+ + OrcArmy()
+ + getDescription() : String
+ }
+ class OrcCastle {
+ ~ DESCRIPTION : String {static}
+ + OrcCastle()
+ + getDescription() : String
+ }
+ class OrcKing {
+ ~ DESCRIPTION : String {static}
+ + OrcKing()
+ + getDescription() : String
+ }
+ class OrcKingdomFactory {
+ + OrcKingdomFactory()
+ + createArmy() : Army
+ + createCastle() : Castle
+ + createKing() : King
+ }
+}
+KingdomType ..+ FactoryMaker
+App --> "-castle" Castle
+FactoryMaker ..+ App
+App --> "-king" King
+App --> "-army" Army
+ElfArmy ..|> Army
+ElfCastle ..|> Castle
+ElfKing ..|> King
+ElfKingdomFactory ..|> KingdomFactory
+OrcArmy ..|> Army
+OrcCastle ..|> Castle
+OrcKing ..|> King
+OrcKingdomFactory ..|> KingdomFactory
+@enduml
\ No newline at end of file
diff --git a/abstract-factory/etc/abstract-factory_1.png b/abstract-factory/etc/abstract-factory_1.png
deleted file mode 100644
index 5990edd83126..000000000000
Binary files a/abstract-factory/etc/abstract-factory_1.png and /dev/null differ
diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml
index 0e3be76d5894..60fbf72eff54 100644
--- a/abstract-factory/pom.xml
+++ b/abstract-factory/pom.xml
@@ -1,18 +1,72 @@
-
-
+
+
+
4.0.0
com.iluwatar
java-design-patterns
- 1.4.0
+ 1.26.0-SNAPSHOT
abstract-factory
- junit
- junit
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
test
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.abstractfactory.App
+
+
+
+
+
+
+
+
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java
index 3f77a521cdf5..798cbe4fd118 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java
@@ -1,29 +1,86 @@
-package com.iluwatar.abstractfactory;
-
-/**
- *
- * The essence of the Abstract Factory pattern is a factory interface
- * (KingdomFactory) and its implementations (ElfKingdomFactory,
- * OrcKingdomFactory).
- *
- * The example uses both concrete implementations to create a king, a castle and
- * an army.
- *
- */
-public class App {
-
- public static void main(String[] args) {
- createKingdom(new ElfKingdomFactory());
- createKingdom(new OrcKingdomFactory());
- }
-
- public static void createKingdom(KingdomFactory factory) {
- King king = factory.createKing();
- Castle castle = factory.createCastle();
- Army army = factory.createArmy();
- System.out.println("The kingdom was created.");
- System.out.println(king);
- System.out.println(castle);
- System.out.println(army);
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * The Abstract Factory pattern provides a way to encapsulate a group of individual factories that
+ * have a common theme without specifying their concrete classes. In normal usage, the client
+ * software creates a concrete implementation of the abstract factory and then uses the generic
+ * interface of the factory to create the concrete objects that are part of the theme. The client
+ * does not know (or care) which concrete objects it gets from each of these internal factories,
+ * since it uses only the generic interfaces of their products. This pattern separates the details
+ * of implementation of a set of objects from their general usage and relies on object composition,
+ * as object creation is implemented in methods exposed in the factory interface.
+ *
+ * The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory})
+ * and its implementations ( {@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses
+ * both concrete implementations to create a king, a castle, and an army.
+ */
+@Slf4j
+@Getter
+public class App implements Runnable {
+
+ private final Kingdom kingdom = new Kingdom();
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+ var app = new App();
+ app.run();
+ }
+
+ @Override
+ public void run() {
+ LOGGER.info("elf kingdom");
+ createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+ LOGGER.info(kingdom.getArmy().getDescription());
+ LOGGER.info(kingdom.getCastle().getDescription());
+ LOGGER.info(kingdom.getKing().getDescription());
+
+ LOGGER.info("orc kingdom");
+ createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ LOGGER.info(kingdom.getArmy().getDescription());
+ LOGGER.info(kingdom.getCastle().getDescription());
+ LOGGER.info(kingdom.getKing().getDescription());
+ }
+
+ /**
+ * Creates kingdom.
+ *
+ * @param kingdomType type of Kingdom
+ */
+ public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) {
+ final KingdomFactory kingdomFactory = Kingdom.FactoryMaker.makeFactory(kingdomType);
+ kingdom.setKing(kingdomFactory.createKing());
+ kingdom.setCastle(kingdomFactory.createCastle());
+ kingdom.setArmy(kingdomFactory.createArmy());
+ }
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java
index 39e023e3bebf..78c75323f1c0 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java
@@ -1,5 +1,31 @@
-package com.iluwatar.abstractfactory;
-
-public interface Army {
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** Army interface. */
+public interface Army {
+
+ String getDescription();
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java
index 277daea56f11..ee1e16f3cd3f 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java
@@ -1,5 +1,31 @@
-package com.iluwatar.abstractfactory;
-
-public interface Castle {
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** Castle interface. */
+public interface Castle {
+
+ String getDescription();
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java
index 4731062228e0..d7e46c1456f0 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java
@@ -1,10 +1,36 @@
-package com.iluwatar.abstractfactory;
-
-public class ElfArmy implements Army {
-
- @Override
- public String toString() {
- return "This is the Elven Army!";
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** ElfArmy. */
+public class ElfArmy implements Army {
+
+ static final String DESCRIPTION = "This is the elven army!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java
index 851baf84f6ce..136afb11fd23 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java
@@ -1,10 +1,36 @@
-package com.iluwatar.abstractfactory;
-
-public class ElfCastle implements Castle {
-
- @Override
- public String toString() {
- return "This is the Elven castle!";
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** ElfCastle. */
+public class ElfCastle implements Castle {
+
+ static final String DESCRIPTION = "This is the elven castle!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java
index eafaccf49605..9b0d3a6f1a77 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java
@@ -1,10 +1,36 @@
-package com.iluwatar.abstractfactory;
-
-public class ElfKing implements King {
-
- @Override
- public String toString() {
- return "This is the Elven king!";
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** ElfKing. */
+public class ElfKing implements King {
+
+ static final String DESCRIPTION = "This is the elven king!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java
index eb4b996858bf..b09a2f47c252 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java
@@ -1,22 +1,44 @@
-package com.iluwatar.abstractfactory;
-
-/**
- *
- * Concrete factory.
- *
- */
-public class ElfKingdomFactory implements KingdomFactory {
-
- public Castle createCastle() {
- return new ElfCastle();
- }
-
- public King createKing() {
- return new ElfKing();
- }
-
- public Army createArmy() {
- return new ElfArmy();
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** ElfKingdomFactory concrete factory. */
+public class ElfKingdomFactory implements KingdomFactory {
+
+ @Override
+ public Castle createCastle() {
+ return new ElfCastle();
+ }
+
+ @Override
+ public King createKing() {
+ return new ElfKing();
+ }
+
+ @Override
+ public Army createArmy() {
+ return new ElfArmy();
+ }
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java
index 12a9c1f13b07..9f65ed434577 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java
@@ -1,5 +1,31 @@
-package com.iluwatar.abstractfactory;
-
-public interface King {
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** King interface. */
+public interface King {
+
+ String getDescription();
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java
new file mode 100644
index 000000000000..d1f85a6a4812
--- /dev/null
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java
@@ -0,0 +1,56 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/** Helper class to manufacture {@link KingdomFactory} beans. */
+@Getter
+@Setter
+public class Kingdom {
+
+ private King king;
+ private Castle castle;
+ private Army army;
+
+ /** The factory of kingdom factories. */
+ public static class FactoryMaker {
+
+ /** Enumeration for the different types of Kingdoms. */
+ public enum KingdomType {
+ ELF,
+ ORC
+ }
+
+ /** The factory method to create KingdomFactory concrete objects. */
+ public static KingdomFactory makeFactory(KingdomType type) {
+ return switch (type) {
+ case ELF -> new ElfKingdomFactory();
+ case ORC -> new OrcKingdomFactory();
+ };
+ }
+ }
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java
index e5b4b4050dee..199c6697dcaa 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java
@@ -1,16 +1,35 @@
-package com.iluwatar.abstractfactory;
-
-/**
- *
- * The factory interface.
- *
- */
-public interface KingdomFactory {
-
- Castle createCastle();
-
- King createKing();
-
- Army createArmy();
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** KingdomFactory factory interface. */
+public interface KingdomFactory {
+
+ Castle createCastle();
+
+ King createKing();
+
+ Army createArmy();
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java
index b0e202d51f6b..31ed6896d921 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java
@@ -1,10 +1,36 @@
-package com.iluwatar.abstractfactory;
-
-public class OrcArmy implements Army {
-
- @Override
- public String toString() {
- return "This is the Orcish Army!";
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** OrcArmy. */
+public class OrcArmy implements Army {
+
+ static final String DESCRIPTION = "This is the orc army!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java
index 785884a5989f..bdae5709a97c 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java
@@ -1,10 +1,36 @@
-package com.iluwatar.abstractfactory;
-
-public class OrcCastle implements Castle {
-
- @Override
- public String toString() {
- return "This is the Orcish castle!";
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** OrcCastle. */
+public class OrcCastle implements Castle {
+
+ static final String DESCRIPTION = "This is the orc castle!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java
index 27ea8afd4e62..7f106d45a01d 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java
@@ -1,10 +1,36 @@
-package com.iluwatar.abstractfactory;
-
-public class OrcKing implements King {
-
- @Override
- public String toString() {
- return "This is the Orc king!";
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** OrcKing. */
+public class OrcKing implements King {
+
+ static final String DESCRIPTION = "This is the orc king!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java
index 2f2a2a54d9a3..82d258570460 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java
@@ -1,22 +1,44 @@
-package com.iluwatar.abstractfactory;
-
-/**
- *
- * Concrete factory.
- *
- */
-public class OrcKingdomFactory implements KingdomFactory {
-
- public Castle createCastle() {
- return new OrcCastle();
- }
-
- public King createKing() {
- return new OrcKing();
- }
-
- public Army createArmy() {
- return new OrcArmy();
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+/** OrcKingdomFactory concrete factory. */
+public class OrcKingdomFactory implements KingdomFactory {
+
+ @Override
+ public Castle createCastle() {
+ return new OrcCastle();
+ }
+
+ @Override
+ public King createKing() {
+ return new OrcKing();
+ }
+
+ @Override
+ public Army createArmy() {
+ return new OrcArmy();
+ }
+}
diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
new file mode 100644
index 000000000000..b5dde940c464
--- /dev/null
+++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
@@ -0,0 +1,113 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests for abstract factory. */
+class AbstractFactoryTest {
+
+ private final App app = new App();
+
+ @Test
+ void verifyKingCreation() {
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+ final var kingdom = app.getKingdom();
+
+ final var elfKing = kingdom.getKing();
+ assertTrue(elfKing instanceof ElfKing);
+ assertEquals(ElfKing.DESCRIPTION, elfKing.getDescription());
+
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var orcKing = kingdom.getKing();
+ assertTrue(orcKing instanceof OrcKing);
+ assertEquals(OrcKing.DESCRIPTION, orcKing.getDescription());
+ }
+
+ @Test
+ void verifyCastleCreation() {
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+ final var kingdom = app.getKingdom();
+
+ final var elfCastle = kingdom.getCastle();
+ assertTrue(elfCastle instanceof ElfCastle);
+ assertEquals(ElfCastle.DESCRIPTION, elfCastle.getDescription());
+
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var orcCastle = kingdom.getCastle();
+ assertTrue(orcCastle instanceof OrcCastle);
+ assertEquals(OrcCastle.DESCRIPTION, orcCastle.getDescription());
+ }
+
+ @Test
+ void verifyArmyCreation() {
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+ final var kingdom = app.getKingdom();
+
+ final var elfArmy = kingdom.getArmy();
+ assertTrue(elfArmy instanceof ElfArmy);
+ assertEquals(ElfArmy.DESCRIPTION, elfArmy.getDescription());
+
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var orcArmy = kingdom.getArmy();
+ assertTrue(orcArmy instanceof OrcArmy);
+ assertEquals(OrcArmy.DESCRIPTION, orcArmy.getDescription());
+ }
+
+ @Test
+ void verifyElfKingdomCreation() {
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+ final var kingdom = app.getKingdom();
+
+ final var king = kingdom.getKing();
+ final var castle = kingdom.getCastle();
+ final var army = kingdom.getArmy();
+ assertTrue(king instanceof ElfKing);
+ assertEquals(ElfKing.DESCRIPTION, king.getDescription());
+ assertTrue(castle instanceof ElfCastle);
+ assertEquals(ElfCastle.DESCRIPTION, castle.getDescription());
+ assertTrue(army instanceof ElfArmy);
+ assertEquals(ElfArmy.DESCRIPTION, army.getDescription());
+ }
+
+ @Test
+ void verifyOrcKingdomCreation() {
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var kingdom = app.getKingdom();
+
+ final var king = kingdom.getKing();
+ final var castle = kingdom.getCastle();
+ final var army = kingdom.getArmy();
+ assertTrue(king instanceof OrcKing);
+ assertEquals(OrcKing.DESCRIPTION, king.getDescription());
+ assertTrue(castle instanceof OrcCastle);
+ assertEquals(OrcCastle.DESCRIPTION, castle.getDescription());
+ assertTrue(army instanceof OrcArmy);
+ assertEquals(OrcArmy.DESCRIPTION, army.getDescription());
+ }
+}
diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java
index 0241ed8ce960..9f53691a594d 100644
--- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java
+++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java
@@ -1,13 +1,39 @@
-package com.iluwatar.abstractfactory;
-import org.junit.Test;
-
-import com.iluwatar.abstractfactory.App;
-
-public class AppTest {
-
- @Test
- public void test() {
- String[] args = {};
- App.main(args);
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.abstractfactory;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Check whether the execution of the main method in {@link App} throws an exception. */
+class AppTest {
+
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/active-object/README.md b/active-object/README.md
new file mode 100644
index 000000000000..a804ce9e632b
--- /dev/null
+++ b/active-object/README.md
@@ -0,0 +1,222 @@
+---
+title: "Active Object Pattern in Java: Achieving Efficient Asynchronous Processing"
+shortTitle: Active Object
+description: "Learn about the Active Object design pattern in Java. This guide covers asynchronous behavior, concurrency, and practical examples to enhance your Java applications' performance."
+category: Concurrency
+language: en
+tag:
+ - Asynchronous
+ - Decoupling
+ - Messaging
+ - Synchronization
+ - Thread management
+---
+
+## Intent of Active Object Design Pattern
+
+The Active Object pattern provides a reliable method for asynchronous processing in Java, ensuring responsive applications and efficient thread management. It achieves this by encapsulating tasks within objects that have their own thread and message queue. This separation keeps the main thread responsive and avoids issues like direct thread manipulation or shared state access.
+
+## Detailed Explanation of Active Object Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a busy restaurant where customers place orders with waiters. Instead of the waiters going to the kitchen to prepare the food themselves, they write the orders on slips and hand them to a dispatcher. The dispatcher manages a pool of chefs who prepare the meals asynchronously. Once a chef is free, they pick up the next order from the queue, prepare the dish, and notify the waiter when it's ready for serving.
+>
+> In this analogy, the waiters represent the client threads, the dispatcher represents the scheduler, and the chefs represent the method execution in separate threads. This setup allows the waiters to continue taking orders without being blocked by the food preparation process, much like the Active Object pattern decouples method invocation from execution to enhance concurrency.
+
+In plain words
+
+> The Active Object pattern decouples method execution from method invocation to improve concurrency and responsiveness in multithreaded applications.
+
+Wikipedia says
+
+> The active object design pattern decouples method execution from method invocation for objects that each reside in their own thread of control.[1] The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests.
+>
+> The pattern consists of six elements:
+>
+> * A proxy, which provides an interface towards clients with publicly accessible methods.
+> * An interface which defines the method request on an active object.
+> * A list of pending requests from clients.
+> * A scheduler, which decides which request to execute next.
+> * The implementation of the active object method.
+> * A callback or variable for the client to receive the result.
+
+Sequence diagram
+
+
+
+
+## Programmatic Example of Active Object in Java
+
+This section explains how the Active Object design pattern works in Java, highlighting its use in asynchronous task management and concurrency control.
+
+The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior. To implement a creature that has its own thread of control mechanism and expose its API only and not the execution itself, we can use the Active Object pattern.
+
+```java
+public abstract class ActiveCreature {
+ private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
+
+ private BlockingQueue requests;
+
+ private String name;
+
+ private Thread thread;
+
+ public ActiveCreature(String name) {
+ this.name = name;
+ this.requests = new LinkedBlockingQueue();
+ thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ requests.take().run();
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage());
+ }
+ }
+ }
+ }
+ );
+ thread.start();
+ }
+
+ public void eat() throws InterruptedException {
+ requests.put(new Runnable() {
+ @Override
+ public void run() {
+ logger.info("{} is eating!", name());
+ logger.info("{} has finished eating!", name());
+ }
+ }
+ );
+ }
+
+ public void roam() throws InterruptedException {
+ requests.put(new Runnable() {
+ @Override
+ public void run() {
+ logger.info("{} has started to roam the wastelands.", name());
+ }
+ }
+ );
+ }
+
+ public String name() {
+ return this.name;
+ }
+}
+```
+
+We can see that any class that will extend the `ActiveCreature` class will have its own thread of control to invoke and execute methods.
+
+For example, the `Orc` class:
+
+```java
+public class Orc extends ActiveCreature {
+
+ public Orc(String name) {
+ super(name);
+ }
+}
+```
+
+Now, we can create multiple creatures such as orcs, tell them to eat and roam, and they will execute it on their own thread of control:
+
+```java
+public class App implements Runnable {
+
+ private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
+
+ private static final int NUM_CREATURES = 3;
+
+ public static void main(String[] args) {
+ var app = new App();
+ app.run();
+ }
+
+ @Override
+ public void run() {
+ List creatures = new ArrayList<>();
+ try {
+ for (int i = 0; i < NUM_CREATURES; i++) {
+ creatures.add(new Orc(Orc.class.getSimpleName() + i));
+ creatures.get(i).eat();
+ creatures.get(i).roam();
+ }
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage());
+ Thread.currentThread().interrupt();
+ } finally {
+ for (int i = 0; i < NUM_CREATURES; i++) {
+ creatures.get(i).kill(0);
+ }
+ }
+ }
+}
+```
+
+Program output:
+
+```
+09:00:02.501 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 is eating!
+09:00:02.501 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 is eating!
+09:00:02.501 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 is eating!
+09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has finished eating!
+09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has finished eating!
+09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has started to roam in the wastelands.
+09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has finished eating!
+09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has started to roam in the wastelands.
+09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has started to roam in the wastelands.
+```
+
+## When to Use the Active Object Pattern in Java
+
+Use the Active Object pattern in Java when:
+
+* when you need to handle asynchronous tasks without blocking the main thread, ensuring better performance and responsiveness.
+* When you need to interact with external resources asynchronously.
+* When you want to improve the responsiveness of your application.
+* When you need to manage concurrent tasks in a modular and maintainable way.
+
+## Active Object Pattern Java Tutorials
+
+* [Android and Java Concurrency: The Active Object Pattern(Douglas Schmidt)](https://www.youtube.com/watch?v=Cd8t2u5Qmvc)
+
+## Real-World Applications of Active Object Pattern in Java
+
+* Real-time trading systems where transaction requests are handled asynchronously.
+* GUIs where long-running tasks are executed in the background without freezing the user interface.
+* Game programming to handle concurrent updates to game state or AI computations.
+
+## Benefits and Trade-offs of Active Object Pattern
+
+Discover the benefits and trade-offs of using the Active Object pattern in Java, including improved thread safety and potential overhead concerns.
+
+Benefits:
+
+* Improves responsiveness of the main thread.
+* Encapsulates concurrency concerns within objects.
+* Promotes better code organization and maintainability.
+* Provides thread safety and avoids shared state access problems.
+
+Trade-offs:
+
+* Introduces additional overhead due to message passing and thread management.
+* May not be suitable for all types of concurrency problems.
+
+## Related Java Design Patterns
+
+* [Command](https://java-design-patterns.com/patterns/command/): Encapsulates a request as an object, similarly to how the Active Object pattern encapsulates method calls.
+* [Promise](https://java-design-patterns.com/patterns/promise/): Provides a means to retrieve the result of an asynchronous method call, often used in conjunction with Active Object.
+* [Proxy](https://java-design-patterns.com/patterns/proxy/): The Active Object pattern can use a proxy to handle method invocations asynchronously.
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object Software](https://amzn.to/3HYqrBE)
+* [Concurrent Programming in Java: Design Principles and Patterns](https://amzn.to/498SRVq)
+* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
+* [Learning Concurrent Programming in Scala](https://amzn.to/3UE07nV)
+* [Pattern Languages of Program Design 3](https://amzn.to/3OI1j61)
+* [Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://amzn.to/3UgC24V)
diff --git a/active-object/etc/active-object-sequence-diagram.png b/active-object/etc/active-object-sequence-diagram.png
new file mode 100644
index 000000000000..b725d9b07b6d
Binary files /dev/null and b/active-object/etc/active-object-sequence-diagram.png differ
diff --git a/active-object/etc/active-object.urm.png b/active-object/etc/active-object.urm.png
new file mode 100644
index 000000000000..c14f66144ee2
Binary files /dev/null and b/active-object/etc/active-object.urm.png differ
diff --git a/active-object/etc/active-object.urm.puml b/active-object/etc/active-object.urm.puml
new file mode 100644
index 000000000000..3fc3c8e1e921
--- /dev/null
+++ b/active-object/etc/active-object.urm.puml
@@ -0,0 +1,25 @@
+@startuml
+package com.iluwatar.activeobject {
+ abstract class ActiveCreature {
+ - logger : Logger
+ - name : String
+ - requests : BlockingQueue
+ - thread : Thread
+ + ActiveCreature(name : String)
+ + eat()
+ + name() : String
+ + roam()
+ }
+ class App {
+ - creatures : Integer
+ - logger : Logger
+ + App()
+ + main(args : String[]) {static}
+ + run()
+ }
+ class Orc {
+ + Orc(name : String)
+ }
+}
+Orc --|> ActiveCreature
+@enduml
\ No newline at end of file
diff --git a/active-object/pom.xml b/active-object/pom.xml
new file mode 100644
index 000000000000..aa26dbbe2e48
--- /dev/null
+++ b/active-object/pom.xml
@@ -0,0 +1,72 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ active-object
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.activeobject.App
+
+
+
+
+
+
+
+
+
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java
new file mode 100644
index 000000000000..5a440020c0ac
--- /dev/null
+++ b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java
@@ -0,0 +1,118 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.activeobject;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** ActiveCreature class is the base of the active object example. */
+public abstract class ActiveCreature {
+
+ private static final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
+
+ private BlockingQueue requests;
+
+ private String name;
+
+ private Thread thread; // Thread of execution.
+
+ private int status; // status of the thread of execution.
+
+ /** Constructor and initialization. */
+ protected ActiveCreature(String name) {
+ this.name = name;
+ this.status = 0;
+ this.requests = new LinkedBlockingQueue<>();
+ thread =
+ new Thread(
+ () -> {
+ boolean infinite = true;
+ while (infinite) {
+ try {
+ requests.take().run();
+ } catch (InterruptedException e) {
+ if (this.status != 0) {
+ logger.error("Thread was interrupted. --> {}", e.getMessage());
+ }
+ infinite = false;
+ Thread.currentThread().interrupt();
+ }
+ }
+ });
+ thread.start();
+ }
+
+ /**
+ * Eats the porridge.
+ *
+ * @throws InterruptedException due to firing a new Runnable.
+ */
+ public void eat() throws InterruptedException {
+ requests.put(
+ () -> {
+ logger.info("{} is eating!", name());
+ logger.info("{} has finished eating!", name());
+ });
+ }
+
+ /**
+ * Roam the wastelands.
+ *
+ * @throws InterruptedException due to firing a new Runnable.
+ */
+ public void roam() throws InterruptedException {
+ requests.put(() -> logger.info("{} has started to roam in the wastelands.", name()));
+ }
+
+ /**
+ * Returns the name of the creature.
+ *
+ * @return the name of the creature.
+ */
+ public String name() {
+ return this.name;
+ }
+
+ /**
+ * Kills the thread of execution.
+ *
+ * @param status of the thread of execution. 0 == OK, the rest is logging an error.
+ */
+ public void kill(int status) {
+ this.status = status;
+ this.thread.interrupt();
+ }
+
+ /**
+ * Returns the status of the thread of execution.
+ *
+ * @return the status of the thread of execution.
+ */
+ public int getStatus() {
+ return this.status;
+ }
+}
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/App.java b/active-object/src/main/java/com/iluwatar/activeobject/App.java
new file mode 100644
index 000000000000..ca3a5526ebb8
--- /dev/null
+++ b/active-object/src/main/java/com/iluwatar/activeobject/App.java
@@ -0,0 +1,75 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.activeobject;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Active Object pattern helps to solve synchronization difficulties without using
+ * 'synchronized' methods. The active object will contain a thread-safe data structure (such as
+ * BlockingQueue) and use to synchronize method calls by moving the logic of the method into an
+ * invocator(usually a Runnable) and store it in the DSA.
+ *
+ * In this example, we fire 20 threads to modify a value in the target class.
+ */
+public class App implements Runnable {
+
+ private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
+
+ private static final int NUM_CREATURES = 3;
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line arguments.
+ */
+ public static void main(String[] args) {
+ var app = new App();
+ app.run();
+ }
+
+ @Override
+ public void run() {
+ List creatures = new ArrayList<>();
+ try {
+ for (int i = 0; i < NUM_CREATURES; i++) {
+ creatures.add(new Orc(Orc.class.getSimpleName() + i));
+ creatures.get(i).eat();
+ creatures.get(i).roam();
+ }
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage());
+ Thread.currentThread().interrupt();
+ } finally {
+ for (int i = 0; i < NUM_CREATURES; i++) {
+ creatures.get(i).kill(0);
+ }
+ }
+ }
+}
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/Orc.java b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
new file mode 100644
index 000000000000..30adde034de5
--- /dev/null
+++ b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
@@ -0,0 +1,33 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.activeobject;
+
+/** An implementation of the ActiveCreature class. */
+public class Orc extends ActiveCreature {
+
+ public Orc(String name) {
+ super(name);
+ }
+}
diff --git a/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java
new file mode 100644
index 000000000000..be79e2fb5527
--- /dev/null
+++ b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java
@@ -0,0 +1,42 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.activeobject;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class ActiveCreatureTest {
+
+ @Test
+ void executionTest() throws InterruptedException {
+ ActiveCreature orc = new Orc("orc1");
+ assertEquals("orc1", orc.name());
+ assertEquals(0, orc.getStatus());
+ orc.eat();
+ orc.roam();
+ orc.kill(0);
+ }
+}
diff --git a/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java b/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java
new file mode 100644
index 000000000000..559e2a1f58f1
--- /dev/null
+++ b/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.activeobject;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+class AppTest {
+
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/actor-model/README.md b/actor-model/README.md
new file mode 100644
index 000000000000..be8065ffefef
--- /dev/null
+++ b/actor-model/README.md
@@ -0,0 +1,201 @@
+---
+title: "Actor Model Pattern in Java: Building Concurrent Systems with Elegance"
+shortTitle: Actor Model
+description: "Explore the Actor Model pattern in Java with real-world examples and practical implementation. Learn how to build scalable, message-driven systems using actors, messages, and asynchronous communication."
+category: Concurrency
+language: en
+tag:
+ - Concurrency
+ - Messaging
+ - Isolation
+ - Asynchronous
+ - Distributed Systems
+ - Actor Model
+---
+
+## Also Known As
+
+- Message-passing concurrency
+- Actor-based concurrency
+
+---
+
+## Intent of Actor Model Pattern
+
+The Actor Model pattern enables the construction of highly concurrent, distributed, and fault-tolerant systems by using isolated components (actors) that interact exclusively through asynchronous message passing.
+
+---
+
+## Detailed Explanation of Actor Model Pattern with Real-World Examples
+
+### 📦 Real-world Example
+
+Imagine a customer service system:
+- Each **customer support agent** is an **actor**.
+- Customers **send questions (messages)** to agents.
+- Each agent handles one request at a time and can **respond asynchronously** without interfering with other agents.
+
+---
+
+### 🧠 In Plain Words
+
+> "Actors are like independent workers that never share memory and only communicate through messages."
+
+---
+
+### 📖 Wikipedia Says
+
+> [Actor model](https://en.wikipedia.org/wiki/Actor_model) is a mathematical model of concurrent computation that treats "actors" as the universal primitives of concurrent computation.
+
+---
+
+### 🧹 Architecture Diagram
+
+
+
+---
+
+## Programmatic Example of Actor Model Pattern in Java
+
+### Actor.java
+
+```java
+public abstract class Actor implements Runnable {
+
+ @Setter @Getter private String actorId;
+ private final BlockingQueue mailbox = new LinkedBlockingQueue<>();
+ private volatile boolean active = true;
+
+
+ public void send(Message message) {
+ mailbox.add(message);
+ }
+
+ public void stop() {
+ active = false;
+ }
+
+ @Override
+ public void run() {
+
+ }
+
+ protected abstract void onReceive(Message message);
+}
+
+```
+
+### Message.java
+
+```java
+
+@AllArgsConstructor
+@Getter
+@Setter
+public class Message {
+ private final String content;
+ private final String senderId;
+}
+```
+
+### ActorSystem.java
+
+```java
+public class ActorSystem {
+ public void startActor(Actor actor) {
+ String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
+ actor.setActorId(actorId); // assign the actor it's ID
+ actorRegister.put(actorId, actor); // Register and save the actor with it's ID
+ executor.submit(actor); // Run the actor in a thread
+ }
+ public Actor getActorById(String actorId) {
+ return actorRegister.get(actorId); // Find by Id
+ }
+
+ public void shutdown() {
+ executor.shutdownNow(); // Stop all threads
+ }
+}
+```
+
+### App.java
+
+```java
+public class App {
+ public static void main(String[] args) {
+ ActorSystem system = new ActorSystem();
+ Actor srijan = new ExampleActor(system);
+ Actor ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+ srijan.send(new Message("Hello srijan!", ansh.getActorId()));
+
+ Thread.sleep(1000); // Give time for messages to process
+
+ srijan.stop(); // Stop the actor gracefully
+ ansh.stop();
+ system.shutdown(); // Stop the actor system
+ }
+}
+```
+
+---
+
+## When to Use the Actor Model Pattern in Java
+
+- When building **concurrent or distributed systems**
+- When you want **no shared mutable state**
+- When you need **asynchronous, message-driven communication**
+- When components should be **isolated and loosely coupled**
+
+---
+
+## Actor Model Pattern Java Tutorials
+
+- [Baeldung – Akka with Java](https://www.baeldung.com/java-akka)
+- [Vaughn Vernon – Reactive Messaging Patterns](https://vaughnvernon.co/?p=1143)
+
+---
+
+## Real-World Applications of Actor Model Pattern in Java
+
+- [Akka Framework](https://akka.io/)
+- [Erlang and Elixir concurrency](https://www.erlang.org/)
+- [Microsoft Orleans](https://learn.microsoft.com/en-us/dotnet/orleans/)
+- JVM-based game engines and simulators
+
+---
+
+## Benefits and Trade-offs of Actor Model Pattern
+
+### ✅ Benefits
+- High concurrency support
+- Easy scaling across threads or machines
+- Fault isolation and recovery
+- Message ordering within actors
+
+### ⚠️ Trade-offs
+- Harder to debug due to asynchronous behavior
+- Slight performance overhead due to message queues
+- More complex to design than simple method calls
+
+---
+
+## Related Java Design Patterns
+
+- [Command Pattern](../command)
+- [Mediator Pattern](../mediator)
+- [Event-Driven Architecture](../event-driven-architecture)
+- [Observer Pattern](../observer)
+
+---
+
+## References and Credits
+
+- *Programming Erlang*, Joe Armstrong
+- *Reactive Design Patterns*, Roland Kuhn
+- *The Actor Model in 10 Minutes*, [InfoQ Article](https://www.infoq.com/articles/actor-model/)
+- [Akka Documentation](https://doc.akka.io/docs/akka/current/index.html)
+
diff --git a/actor-model/etc/Actor_Model_UML_Class_Diagram.png b/actor-model/etc/Actor_Model_UML_Class_Diagram.png
new file mode 100644
index 000000000000..a4c34d7a75fd
Binary files /dev/null and b/actor-model/etc/Actor_Model_UML_Class_Diagram.png differ
diff --git a/actor-model/etc/actor-model.urm.puml b/actor-model/etc/actor-model.urm.puml
new file mode 100644
index 000000000000..020c1fc735a4
--- /dev/null
+++ b/actor-model/etc/actor-model.urm.puml
@@ -0,0 +1,35 @@
+@startuml actor-model
+
+title Actor Model - UML Class Diagram
+
+class ActorSystem {
+ +actorOf(actor: Actor): Actor
+ +shutdown(): void
+}
+
+class Actor {
+ -mailbox: BlockingQueue
+ -active: boolean
+ +send(message: Message): void
+ +stop(): void
+ +run(): void
+ #onReceive(message: Message): void
+}
+
+class ExampleActor {
+ +onReceive(message: Message): void
+}
+
+class Message {
+ -content: String
+ -sender: Actor
+ +getContent(): String
+ +getSender(): Actor
+}
+
+ActorSystem --> Actor : creates
+Actor <|-- ExampleActor : extends
+Actor --> Message : processes
+ExampleActor --> Message : uses
+
+@enduml
diff --git a/actor-model/pom.xml b/actor-model/pom.xml
new file mode 100644
index 000000000000..76c288829b8d
--- /dev/null
+++ b/actor-model/pom.xml
@@ -0,0 +1,114 @@
+
+
+
+
+ 4.0.0
+
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+
+ actor-model
+ Actor Model
+
+
+
+
+
+ org.junit
+ junit-bom
+ 5.11.0
+ pom
+ import
+
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.platform
+ junit-platform-launcher
+ test
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.3.0
+
+
+ jar-with-dependencies
+
+
+
+ com.iluwatar.actormodel.App
+
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
+
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java b/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java
new file mode 100644
index 000000000000..6e2aaccd1937
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java
@@ -0,0 +1,63 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.actormodel;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import lombok.Getter;
+import lombok.Setter;
+
+public abstract class Actor implements Runnable {
+
+ @Setter @Getter private String actorId;
+ private final BlockingQueue mailbox = new LinkedBlockingQueue<>();
+ private volatile boolean active =
+ true; // always read from main memory and written back to main memory,
+
+ // rather than being cached in a thread's local memory. To make it consistent to all Actors
+
+ public void send(Message message) {
+ mailbox.add(message); // Add message to queue
+ }
+
+ public void stop() {
+ active = false; // Stop the actor loop
+ }
+
+ @Override
+ public void run() {
+ while (active) {
+ try {
+ Message message = mailbox.take(); // Wait for a message
+ onReceive(message); // Process it
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ // Child classes must define what to do with a message
+ protected abstract void onReceive(Message message);
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java b/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java
new file mode 100644
index 000000000000..db7c21cb6088
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.actormodel;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ActorSystem {
+ private final ExecutorService executor = Executors.newCachedThreadPool();
+ private final ConcurrentHashMap actorRegister = new ConcurrentHashMap<>();
+ private final AtomicInteger idCounter = new AtomicInteger(0);
+
+ public void startActor(Actor actor) {
+ String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
+ actor.setActorId(actorId); // assign the actor it's ID
+ actorRegister.put(actorId, actor); // Register and save the actor with it's ID
+ executor.submit(actor); // Run the actor in a thread
+ }
+
+ public Actor getActorById(String actorId) {
+ return actorRegister.get(actorId); // Find by Id
+ }
+
+ public void shutdown() {
+ executor.shutdownNow(); // Stop all threads
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/App.java b/actor-model/src/main/java/com/iluwatar/actormodel/App.java
new file mode 100644
index 000000000000..79fe79e48a6f
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/App.java
@@ -0,0 +1,64 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * The Actor Model is a design pattern used to handle concurrency in a safe, scalable, and
+ * message-driven way.
+ *
+ * In the Actor Model: - An **Actor** is an independent unit that has its own state and behavior.
+ * - Actors **communicate only through messages** — they do not share memory. - An **ActorSystem**
+ * is responsible for creating, starting, and managing the lifecycle of actors. - Messages are
+ * delivered asynchronously, and each actor processes them one at a time.
+ *
+ *
💡 Key benefits: - No shared memory = no need for complex thread-safety - Easy to scale with
+ * many actors - Suitable for highly concurrent or distributed systems
+ *
+ *
🔍 This example demonstrates the Actor Model: - `ActorSystem` starts two actors: `srijan` and
+ * `ansh`. - `ExampleActor` and `ExampleActor2` extend the `Actor` class and override the
+ * `onReceive()` method to handle messages. - Actors communicate using `send()` to pass `Message`
+ * objects that include the message content and sender's ID. - The actors process messages
+ * **asynchronously in separate threads**, and we allow a short delay (`Thread.sleep`) to let them
+ * run. - The system is shut down gracefully at the end.
+ */
+package com.iluwatar.actormodel;
+
+public class App {
+ public static void main(String[] args) throws InterruptedException {
+ ActorSystem system = new ActorSystem();
+ Actor srijan = new ExampleActor(system);
+ Actor ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+ srijan.send(new Message("Hello srijan!", ansh.getActorId()));
+
+ Thread.sleep(1000); // Give time for messages to process
+
+ srijan.stop(); // Stop the actor gracefully
+ ansh.stop();
+ system.shutdown(); // Stop the actor system
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java
new file mode 100644
index 000000000000..fd49325f44bd
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.actormodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ExampleActor extends Actor {
+ private final ActorSystem actorSystem;
+ @Getter private final List receivedMessages = new ArrayList<>();
+
+ public ExampleActor(ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ }
+
+ // Logger log = Logger.getLogger(getClass().getName());
+
+ @Override
+ protected void onReceive(Message message) {
+ LOGGER.info(
+ "[{}]Received : {} from : [{}]", getActorId(), message.getContent(), message.getSenderId());
+ Actor sender = actorSystem.getActorById(message.getSenderId()); // sender actor id
+ // Reply of the message
+ if (sender != null && !message.getSenderId().equals(getActorId())) {
+ sender.send(new Message("I got your message ", getActorId()));
+ }
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java
new file mode 100644
index 000000000000..037f96716558
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java
@@ -0,0 +1,46 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.actormodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ExampleActor2 extends Actor {
+ private final ActorSystem actorSystem;
+ @Getter private final List receivedMessages = new ArrayList<>();
+
+ public ExampleActor2(ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ }
+
+ @Override
+ protected void onReceive(Message message) {
+ receivedMessages.add(message.getContent());
+ LOGGER.info("[{}]Received : {}", getActorId(), message.getContent());
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/Message.java b/actor-model/src/main/java/com/iluwatar/actormodel/Message.java
new file mode 100644
index 000000000000..03ca6e02cac0
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/Message.java
@@ -0,0 +1,35 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.actormodel;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public class Message {
+ private final String content;
+ private final String senderId;
+}
diff --git a/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java b/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java
new file mode 100644
index 000000000000..a4a0dee569ab
--- /dev/null
+++ b/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java
@@ -0,0 +1,63 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.actor;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.iluwatar.actormodel.ActorSystem;
+import com.iluwatar.actormodel.App;
+import com.iluwatar.actormodel.ExampleActor;
+import com.iluwatar.actormodel.ExampleActor2;
+import com.iluwatar.actormodel.Message;
+import org.junit.jupiter.api.Test;
+
+public class ActorModelTest {
+ @Test
+ void testMainMethod() throws InterruptedException {
+ App.main(new String[] {});
+ }
+
+ @Test
+ public void testMessagePassing() throws InterruptedException {
+ ActorSystem system = new ActorSystem();
+
+ ExampleActor srijan = new ExampleActor(system);
+ ExampleActor2 ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+
+ // Ansh recieves a message from Srijan
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+
+ // Wait briefly to allow async processing
+ Thread.sleep(200);
+
+ // Check that Srijan received the message
+ assertTrue(
+ ansh.getReceivedMessages().contains("Hello ansh"),
+ "ansh should receive the message from Srijan");
+ }
+}
diff --git a/acyclic-visitor/README.md b/acyclic-visitor/README.md
new file mode 100644
index 000000000000..fb57d5681fd4
--- /dev/null
+++ b/acyclic-visitor/README.md
@@ -0,0 +1,186 @@
+---
+title: "Acyclic Visitor Pattern in Java: Streamlining Object Interactions"
+shortTitle: Acyclic Visitor
+description: "Learn about the Acyclic Visitor pattern in Java. This guide explains how it decouples operations from object hierarchies, providing examples and real-world applications."
+category: Behavioral
+language: en
+tag:
+ - Decoupling
+ - Extensibility
+ - Interface
+ - Object composition
+---
+
+## Intent of Acyclic Visitor Design Pattern
+
+The Acyclic Visitor pattern in Java decouples operations from an object hierarchy, providing a flexible design for various applications.
+
+## Detailed Explanation of Acyclic Visitor Pattern with Real-World Examples
+
+Real-world example
+
+> An analogous real-world example of the Acyclic Visitor pattern in Java is a museum guide system, demonstrating the practical application of this design pattern. Imagine a museum with various exhibits like paintings, sculptures, and historical artifacts. The museum has different types of guides (audio guide, human guide, virtual reality guide) that provide information about each exhibit. Instead of modifying the exhibits every time a new guide type is introduced, each guide implements an interface to visit different exhibit types. This way, the museum can add new types of guides without altering the existing exhibits, ensuring that the system remains extensible and maintainable without forming any dependency cycles.
+
+In plain words
+
+> Acyclic Visitor allows functions to be added to existing class hierarchies without modifying the hierarchies.
+
+[WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) says
+
+> The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating the dependency cycles that are inherent to the GangOfFour VisitorPattern.
+
+Sequence diagram
+
+
+
+
+## Programmatic Example of Acyclic Visitor in Java
+
+In this Java example, we have a hierarchy of modem classes illustrating the Acyclic Visitor pattern. The modems in this hierarchy need to be visited by an external algorithm based on filtering criteria (is it Unix or DOS compatible modem).
+
+Here's the `Modem` hierarchy.
+
+```java
+public abstract class Modem {
+ public abstract void accept(ModemVisitor modemVisitor);
+}
+
+public class Zoom extends Modem {
+
+ // Other properties and methods...
+
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof ZoomVisitor) {
+ ((ZoomVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
+ }
+ }
+}
+
+public class Hayes extends Modem {
+
+ // Other properties and methods...
+
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof HayesVisitor) {
+ ((HayesVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
+ }
+ }
+}
+```
+
+Next, we introduce the `ModemVisitor` hierarchy.
+
+```java
+public interface ModemVisitor {
+}
+
+public interface HayesVisitor extends ModemVisitor {
+ void visit(Hayes hayes);
+}
+
+public interface ZoomVisitor extends ModemVisitor {
+ void visit(Zoom zoom);
+}
+
+public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
+}
+
+public class ConfigureForDosVisitor implements AllModemVisitor {
+
+ // Other properties and methods...
+
+ @Override
+ public void visit(Hayes hayes) {
+ LOGGER.info(hayes + " used with Dos configurator.");
+ }
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Dos configurator.");
+ }
+}
+
+public class ConfigureForUnixVisitor implements ZoomVisitor {
+
+ // Other properties and methods...
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Unix configurator.");
+ }
+}
+```
+
+Finally, here are the visitors in action.
+
+```java
+public static void main(String[] args) {
+ var conUnix = new ConfigureForUnixVisitor();
+ var conDos = new ConfigureForDosVisitor();
+
+ var zoom = new Zoom();
+ var hayes = new Hayes();
+
+ hayes.accept(conDos); // Hayes modem with Dos configurator
+ zoom.accept(conDos); // Zoom modem with Dos configurator
+ hayes.accept(conUnix); // Hayes modem with Unix configurator
+ zoom.accept(conUnix); // Zoom modem with Unix configurator
+}
+```
+
+Program output:
+
+```
+09:15:11.125 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Hayes modem used with Dos configurator.
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Zoom modem used with Dos configurator.
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.Hayes -- Only HayesVisitor is allowed to visit Hayes modem
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForUnixVisitor -- Zoom modem used with Unix configurator.
+```
+
+## When to Use the Acyclic Visitor Pattern in Java
+
+This pattern can be used:
+
+* When you need to add a new function to an existing hierarchy without the need to alter or affect that hierarchy.
+* When there are functions that operate upon a hierarchy, but which do not belong in the hierarchy itself. e.g. the ConfigureForDOS / ConfigureForUnix / ConfigureForX issue.
+* When you need to perform very different operations on an object depending upon its type.
+* When the visited class hierarchy will be frequently extended with new derivatives of the Element class.
+* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
+
+## Acyclic Visitor Pattern Java Tutorials
+
+* [The Acyclic Visitor Pattern (Code Crafter)](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html)
+
+## Benefits and Trade-offs of Acyclic Visitor Pattern
+
+Benefits:
+
+* Extensible: New operations can be added easily without changing the object structure.
+* Decoupled: Reduces coupling between the objects and the operations on them.
+* No dependency cycles: Ensures acyclic dependencies, improving maintainability and reducing complexity.
+
+Trade-offs:
+
+* Increased complexity: Can introduce additional complexity with the need for multiple visitor interfaces.
+* Maintenance overhead: Modifying the object hierarchy requires updating all visitors.
+
+## Related Java Design Patterns
+
+* [Composite](https://java-design-patterns.com/patterns/composite/): Often used in conjunction with Acyclic Visitor to allow treating individual objects and compositions uniformly.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): Can be used alongside to add responsibilities to objects dynamically.
+* [Visitor](https://java-design-patterns.com/patterns/visitor/): The Acyclic Visitor pattern is a variation of the Visitor pattern that avoids cyclic dependencies.
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
+* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML](https://amzn.to/4bOtzwF)
+* [Acyclic Visitor (Robert C. Martin)](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
+* [Acyclic Visitor (WikiWikiWeb)](https://wiki.c2.com/?AcyclicVisitor)
diff --git a/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png b/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png
new file mode 100644
index 000000000000..a3c2ba56b89f
Binary files /dev/null and b/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png differ
diff --git a/acyclic-visitor/etc/acyclic-visitor.png b/acyclic-visitor/etc/acyclic-visitor.png
new file mode 100644
index 000000000000..7b4df13d80f8
Binary files /dev/null and b/acyclic-visitor/etc/acyclic-visitor.png differ
diff --git a/acyclic-visitor/etc/acyclic-visitor.urm.puml b/acyclic-visitor/etc/acyclic-visitor.urm.puml
new file mode 100644
index 000000000000..e67bbde4407c
--- /dev/null
+++ b/acyclic-visitor/etc/acyclic-visitor.urm.puml
@@ -0,0 +1,53 @@
+@startuml
+package com.iluwatar.acyclicvisitor {
+ interface AllModemVisitor {
+ }
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ class ConfigureForDosVisitor {
+ - LOGGER : Logger {static}
+ + ConfigureForDosVisitor()
+ + visit(hayes : Hayes)
+ + visit(zoom : Zoom)
+ }
+ class ConfigureForUnixVisitor {
+ - LOGGER : Logger {static}
+ + ConfigureForUnixVisitor()
+ + visit(zoom : Zoom)
+ }
+ class Hayes {
+ - LOGGER : Logger {static}
+ + Hayes()
+ + accept(modemVisitor : ModemVisitor)
+ + toString() : String
+ }
+ interface HayesVisitor {
+ + visit(Hayes) {abstract}
+ }
+ abstract class Modem {
+ + Modem()
+ + accept(ModemVisitor) {abstract}
+ }
+ interface ModemVisitor {
+ }
+ class Zoom {
+ - LOGGER : Logger {static}
+ + Zoom()
+ + accept(modemVisitor : ModemVisitor)
+ + toString() : String
+ }
+ interface ZoomVisitor {
+ + visit(Zoom) {abstract}
+ }
+}
+AllModemVisitor --|> ZoomVisitor
+AllModemVisitor --|> HayesVisitor
+ConfigureForDosVisitor ..|> AllModemVisitor
+ConfigureForUnixVisitor ..|> ZoomVisitor
+Hayes --|> Modem
+HayesVisitor --|> ModemVisitor
+Zoom --|> Modem
+ZoomVisitor --|> ModemVisitor
+@enduml
\ No newline at end of file
diff --git a/acyclic-visitor/pom.xml b/acyclic-visitor/pom.xml
new file mode 100644
index 000000000000..b4f5646b71c0
--- /dev/null
+++ b/acyclic-visitor/pom.xml
@@ -0,0 +1,77 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ acyclic-visitor
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.acyclicvisitor.App
+
+
+
+
+
+
+
+
+
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
new file mode 100644
index 000000000000..a3b1679a2d9f
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
@@ -0,0 +1,31 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+/**
+ * All ModemVisitor interface extends all visitor interfaces. This interface provides ease of use
+ * when a visitor needs to visit all modem types.
+ */
+public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
new file mode 100644
index 000000000000..3b7c6cd61e4b
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+/**
+ * The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies
+ * without affecting those hierarchies, and without creating the dependency cycles that are inherent
+ * to the GoF Visitor pattern, by making the Visitor base class degenerate
+ *
+ * In this example the visitor base class is {@link ModemVisitor}. The base class of the visited
+ * hierarchy is {@link Modem} and has two children {@link Hayes} and {@link Zoom} each one having
+ * its own visitor interface {@link HayesVisitor} and {@link ZoomVisitor} respectively. {@link
+ * ConfigureForUnixVisitor} and {@link ConfigureForDosVisitor} implement each derivative's visit
+ * method only if it is required
+ */
+public class App {
+
+ /** Program's entry point. */
+ public static void main(String[] args) {
+ var conUnix = new ConfigureForUnixVisitor();
+ var conDos = new ConfigureForDosVisitor();
+
+ var zoom = new Zoom();
+ var hayes = new Hayes();
+
+ hayes.accept(conDos); // Hayes modem with Dos configurator
+ zoom.accept(conDos); // Zoom modem with Dos configurator
+ hayes.accept(conUnix); // Hayes modem with Unix configurator
+ zoom.accept(conUnix); // Zoom modem with Unix configurator
+ }
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
new file mode 100644
index 000000000000..267a8d66ac45
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
@@ -0,0 +1,44 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos manufacturer.
+ */
+@Slf4j
+public class ConfigureForDosVisitor implements AllModemVisitor {
+
+ @Override
+ public void visit(Hayes hayes) {
+ LOGGER.info(hayes + " used with Dos configurator.");
+ }
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Dos configurator.");
+ }
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
new file mode 100644
index 000000000000..d9fd14f69435
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * ConfigureForUnixVisitor class implements zoom's visit method for Unix manufacturer, unlike
+ * traditional visitor pattern, this class may selectively implement visit for other modems.
+ */
+@Slf4j
+public class ConfigureForUnixVisitor implements ZoomVisitor {
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Unix configurator.");
+ }
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
new file mode 100644
index 000000000000..e0b2fcc2b530
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
@@ -0,0 +1,48 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+import lombok.extern.slf4j.Slf4j;
+
+/** Hayes class implements its accept method. */
+@Slf4j
+public class Hayes implements Modem {
+
+ /** Accepts all visitors but honors only HayesVisitor. */
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof HayesVisitor) {
+ ((HayesVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
+ }
+ }
+
+ /** Hayes' modem's toString method. */
+ @Override
+ public String toString() {
+ return "Hayes modem";
+ }
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
new file mode 100644
index 000000000000..aad9b970994f
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
@@ -0,0 +1,30 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+/** HayesVisitor interface. */
+public interface HayesVisitor extends ModemVisitor {
+ void visit(Hayes hayes);
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
new file mode 100644
index 000000000000..8552574453e5
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
@@ -0,0 +1,30 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+/** //Modem abstract class. converted to an interface */
+public interface Modem {
+ void accept(ModemVisitor modemVisitor);
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java
new file mode 100644
index 000000000000..9391a980cd29
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java
@@ -0,0 +1,33 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+/**
+ * ModemVisitor interface does not contain any visit methods so that it does not depend on the
+ * visited hierarchy. Each derivative's visit method is declared in its own visitor interface
+ */
+public interface ModemVisitor {
+ // Visitor is a degenerate base class for all visitors.
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
new file mode 100644
index 000000000000..59b50a54a12f
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
@@ -0,0 +1,48 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+import lombok.extern.slf4j.Slf4j;
+
+/** Zoom class implements its accept method. */
+@Slf4j
+public class Zoom implements Modem {
+
+ /** Accepts all visitors but honors only ZoomVisitor. */
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof ZoomVisitor) {
+ ((ZoomVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
+ }
+ }
+
+ /** Zoom modem's toString method. */
+ @Override
+ public String toString() {
+ return "Zoom modem";
+ }
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
new file mode 100644
index 000000000000..5388ded6f735
--- /dev/null
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
@@ -0,0 +1,30 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+/** ZoomVisitor interface. */
+public interface ZoomVisitor extends ModemVisitor {
+ void visit(Zoom zoom);
+}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
new file mode 100644
index 000000000000..7a21498a63ea
--- /dev/null
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
@@ -0,0 +1,45 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests that the Acyclic Visitor example runs without errors. */
+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} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
new file mode 100644
index 000000000000..a989d9287921
--- /dev/null
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.Test;
+
+/** Hayes test class */
+class HayesTest {
+
+ @Test
+ void testAcceptForDos() {
+ var hayes = new Hayes();
+ var mockVisitor = mock(ConfigureForDosVisitor.class);
+
+ hayes.accept(mockVisitor);
+ verify((HayesVisitor) mockVisitor).visit(eq(hayes));
+ }
+
+ @Test
+ void testAcceptForUnix() {
+ var hayes = new Hayes();
+ var mockVisitor = mock(ConfigureForUnixVisitor.class);
+
+ hayes.accept(mockVisitor);
+
+ verifyNoMoreInteractions(mockVisitor);
+ }
+}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
new file mode 100644
index 000000000000..d5fe79965d47
--- /dev/null
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.acyclicvisitor;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.junit.jupiter.api.Test;
+
+/** Zoom test class */
+class ZoomTest {
+
+ @Test
+ void testAcceptForDos() {
+ var zoom = new Zoom();
+ var mockVisitor = mock(ConfigureForDosVisitor.class);
+
+ zoom.accept(mockVisitor);
+ verify((ZoomVisitor) mockVisitor).visit(eq(zoom));
+ }
+
+ @Test
+ void testAcceptForUnix() {
+ var zoom = new Zoom();
+ var mockVisitor = mock(ConfigureForUnixVisitor.class);
+
+ zoom.accept(mockVisitor);
+ verify((ZoomVisitor) mockVisitor).visit(eq(zoom));
+ }
+}
diff --git a/adapter/README.md b/adapter/README.md
new file mode 100644
index 000000000000..489742494709
--- /dev/null
+++ b/adapter/README.md
@@ -0,0 +1,154 @@
+---
+title: "Adapter Pattern in Java: Seamless Integration of Incompatible Systems"
+shortTitle: Adapter
+description: "Learn how the Adapter Design Pattern works in Java with detailed examples and use cases. Understand how it enables compatibility between incompatible interfaces."
+category: Structural
+language: en
+tag:
+ - Compatibility
+ - Decoupling
+ - Gang of Four
+ - Interface
+ - Object composition
+ - Wrapping
+---
+
+## Also known as
+
+* Wrapper
+
+## Intent of Adapter Design Pattern
+
+The Adapter Design Pattern in Java converts the interface of a class into another interface that clients expect, enabling compatibility.
+
+## Detailed Explanation of Adapter Pattern with Real-World Examples
+
+Real-world example
+
+> Consider that you have some pictures on your memory card and you need to transfer them to your computer. To transfer them, you need some kind of adapter that is compatible with your computer ports so that you can attach a memory card to your computer. In this case card reader is an adapter. Another example would be the famous power adapter; a three-legged plug can't be connected to a two-pronged outlet, it needs to use a power adapter that makes it compatible with the two-pronged outlets. Yet another example would be a translator translating words spoken by one person to another
+
+In plain words
+
+> Adapter pattern lets you wrap an otherwise incompatible object in an adapter to make it compatible with another class.
+
+Wikipedia says
+
+> In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Adapter Pattern in Java
+
+The Adapter Pattern example in Java shows how a class with an incompatible interface can be adapted to work with another class.
+
+Consider a wannabe captain that can only use rowing boats but can't sail at all.
+
+First, we have interfaces `RowingBoat` and `FishingBoat`
+
+```java
+public interface RowingBoat {
+ void row();
+}
+
+@Slf4j
+public class FishingBoat {
+ public void sail() {
+ LOGGER.info("The fishing boat is sailing");
+ }
+}
+```
+
+The captain expects an implementation of `RowingBoat` interface to be able to move.
+
+```java
+public class Captain {
+
+ private final RowingBoat rowingBoat;
+
+ // default constructor and setter for rowingBoat
+ public Captain(RowingBoat rowingBoat) {
+ this.rowingBoat = rowingBoat;
+ }
+
+ public void row() {
+ rowingBoat.row();
+ }
+}
+```
+
+Now, let's say the pirates are coming and our captain needs to escape but there is only a fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
+
+```java
+@Slf4j
+public class FishingBoatAdapter implements RowingBoat {
+
+ private final FishingBoat boat;
+
+ public FishingBoatAdapter() {
+ boat = new FishingBoat();
+ }
+
+ @Override
+ public void row() {
+ boat.sail();
+ }
+}
+```
+
+Now the `Captain` can use the `FishingBoat` to escape the pirates.
+
+```java
+ public static void main(final String[] args) {
+ // The captain can only operate rowing boats but with adapter he is able to
+ // use fishing boats as well
+ var captain = new Captain(new FishingBoatAdapter());
+ captain.row();
+}
+```
+
+The program outputs:
+
+```
+10:25:08.074 [main] INFO com.iluwatar.adapter.FishingBoat -- The fishing boat is sailing
+```
+
+## When to Use the Adapter Pattern in Java
+
+Use the Adapter pattern in Java when
+
+* You want to use an existing class, and its interface does not match the one you need
+* You want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
+* You need to use several existing subclasses, but it's impractical to adapt their interface by subclassing everyone. An object adapter can adapt the interface of its parent class.
+* Most of the applications using third-party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
+
+## Adapter Pattern Java Tutorials
+
+* [Using the Adapter Design Pattern in Java (Dzone)](https://dzone.com/articles/adapter-design-pattern-in-java)
+* [Adapter in Java (Refactoring Guru)](https://refactoring.guru/design-patterns/adapter/java/example)
+* [The Adapter Pattern in Java (Baeldung)](https://www.baeldung.com/java-adapter-pattern)
+* [Adapter Design Pattern (GeeksForGeeks)](https://www.geeksforgeeks.org/adapter-pattern/)
+
+## Benefits and Trade-offs of Adapter Pattern
+
+Class and object adapters offer different benefits and drawbacks. A class adapter adapts the Adaptee to the Target by binding to a specific Adaptee class, which means it cannot adapt a class and all its subclasses. This type of adapter allows the Adapter to override some of the Adaptee’s behavior because the Adapter is a subclass of the Adaptee. Additionally, it introduces only one object without needing extra pointer indirection to reach the Adaptee.
+
+On the other hand, an object adapter allows a single Adapter to work with multiple Adaptees, including the Adaptee and all its subclasses. This type of adapter can add functionality to all Adaptees simultaneously. However, it makes overriding the Adaptee’s behavior more difficult, as it requires subclassing the Adaptee and having the Adapter refer to this subclass instead of the Adaptee itself.
+
+## Real-World Applications of Adapter Pattern in Java
+
+* `java.io.InputStreamReader` and `java.io.OutputStreamWriter` in the Java IO library.
+* GUI component libraries that allow for plug-ins or adapters to convert between different GUI component interfaces.
+* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
+* [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-)
+* [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-)
+* [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-)
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [J2EE Design Patterns](https://amzn.to/4dpzgmx)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
diff --git a/adapter/etc/adapter-sequence-diagram.png b/adapter/etc/adapter-sequence-diagram.png
new file mode 100644
index 000000000000..a9bb557ea61e
Binary files /dev/null and b/adapter/etc/adapter-sequence-diagram.png differ
diff --git a/adapter/etc/adapter.png b/adapter/etc/adapter.png
deleted file mode 100644
index 511bb5880ee7..000000000000
Binary files a/adapter/etc/adapter.png and /dev/null differ
diff --git a/adapter/etc/adapter.ucls b/adapter/etc/adapter.ucls
deleted file mode 100644
index 8c09f039930c..000000000000
--- a/adapter/etc/adapter.ucls
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/adapter/etc/adapter.urm.png b/adapter/etc/adapter.urm.png
new file mode 100644
index 000000000000..341ad67699d9
Binary files /dev/null and b/adapter/etc/adapter.urm.png differ
diff --git a/adapter/etc/adapter.urm.puml b/adapter/etc/adapter.urm.puml
new file mode 100644
index 000000000000..1277cbb87125
--- /dev/null
+++ b/adapter/etc/adapter.urm.puml
@@ -0,0 +1,31 @@
+@startuml
+package com.iluwatar.adapter {
+ class App {
+ - App()
+ + main(args : String[]) {static}
+ }
+ class Captain {
+ - rowingBoat : RowingBoat
+ + Captain()
+ + Captain(boat : RowingBoat)
+ ~ row()
+ ~ setRowingBoat(boat : RowingBoat)
+ }
+ ~class FishingBoat {
+ - LOGGER : Logger {static}
+ ~ FishingBoat()
+ ~ sail()
+ }
+ class FishingBoatAdapter {
+ - boat : FishingBoat
+ + FishingBoatAdapter()
+ + row()
+ }
+ interface RowingBoat {
+ + row() {abstract}
+ }
+}
+FishingBoatAdapter --> "-boat" FishingBoat
+Captain --> "-rowingBoat" RowingBoat
+FishingBoatAdapter ..|> RowingBoat
+@enduml
\ No newline at end of file
diff --git a/adapter/etc/adapter_1.png b/adapter/etc/adapter_1.png
deleted file mode 100644
index 64eb34b84f79..000000000000
Binary files a/adapter/etc/adapter_1.png and /dev/null differ
diff --git a/adapter/pom.xml b/adapter/pom.xml
index a0f738f34e08..6e7f45a515b5 100644
--- a/adapter/pom.xml
+++ b/adapter/pom.xml
@@ -1,18 +1,77 @@
-
-
+
+
+
4.0.0
com.iluwatar
java-design-patterns
- 1.4.0
+ 1.26.0-SNAPSHOT
adapter
- junit
- junit
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.mockito
+ mockito-core
test
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.adapter.App
+
+
+
+
+
+
+
+
diff --git a/adapter/src/main/java/com/iluwatar/adapter/App.java b/adapter/src/main/java/com/iluwatar/adapter/App.java
index 43a93fa44951..a4fa74274f74 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/App.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/App.java
@@ -1,21 +1,61 @@
-package com.iluwatar.adapter;
-
-/**
- *
- * There are two variations of the Adapter pattern: The class adapter implements
- * the adaptee's interface whereas the object adapter uses composition to
- * contain the adaptee in the adapter object. This example uses the object
- * adapter approach.
- *
- * The Adapter (GnomeEngineer) converts the interface of the target class
- * (GoblinGlider) into a suitable one expected by the client
- * (GnomeEngineeringManager).
- *
- */
-public class App {
-
- public static void main(String[] args) {
- Engineer manager = new GnomeEngineeringManager();
- manager.operateDevice();
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.adapter;
+
+/**
+ * An adapter helps two incompatible interfaces to work together. This is the real world definition
+ * for an adapter. Interfaces may be incompatible but the inner functionality should suit the need.
+ * The Adapter design pattern allows otherwise incompatible classes to work together by converting
+ * the interface of one class into an interface expected by the clients.
+ *
+ * There are two variations of the Adapter pattern: The class adapter implements the adaptee's
+ * interface whereas the object adapter uses composition to contain the adaptee in the adapter
+ * object. This example uses the object adapter approach.
+ *
+ *
The Adapter ({@link FishingBoatAdapter}) converts the interface of the adaptee class ({@link
+ * FishingBoat}) into a suitable one expected by the client ({@link RowingBoat}).
+ *
+ *
The story of this implementation is this.
+ * Pirates are coming! we need a {@link RowingBoat} to flee! We have a {@link FishingBoat} and our
+ * captain. We have no time to make up a new ship! we need to reuse this {@link FishingBoat}. The
+ * captain needs a rowing boat which he can operate. The spec is in {@link RowingBoat}. We will use
+ * the Adapter pattern to reuse {@link FishingBoat}.
+ */
+public final class App {
+
+ private App() {}
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(final String[] args) {
+ // The captain can only operate rowing boats but with adapter he is able to
+ // use fishing boats as well
+ var captain = new Captain(new FishingBoatAdapter());
+ captain.row();
+ }
+}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/Captain.java b/adapter/src/main/java/com/iluwatar/adapter/Captain.java
new file mode 100644
index 000000000000..3b771e9d833e
--- /dev/null
+++ b/adapter/src/main/java/com/iluwatar/adapter/Captain.java
@@ -0,0 +1,45 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.adapter;
+
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * The Captain uses {@link RowingBoat} to sail.
+ * This is the client in the pattern.
+ */
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public final class Captain {
+
+ private RowingBoat rowingBoat;
+
+ void row() {
+ rowingBoat.row();
+ }
+}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/Engineer.java b/adapter/src/main/java/com/iluwatar/adapter/Engineer.java
deleted file mode 100644
index 7478b5b69fa1..000000000000
--- a/adapter/src/main/java/com/iluwatar/adapter/Engineer.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.iluwatar.adapter;
-
-/**
- *
- * Engineers can operate devices.
- *
- */
-public interface Engineer {
-
- void operateDevice();
-
-}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
new file mode 100644
index 000000000000..dd39f88f1ce0
--- /dev/null
+++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
@@ -0,0 +1,39 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.adapter;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Device class (adaptee in the pattern). We want to reuse this class. Fishing boat moves by
+ * sailing.
+ */
+@Slf4j
+final class FishingBoat {
+
+ void sail() {
+ LOGGER.info("The fishing boat is sailing");
+ }
+}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java
new file mode 100644
index 000000000000..8c7e9e88e220
--- /dev/null
+++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java
@@ -0,0 +1,38 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.adapter;
+
+/**
+ * Adapter class. Adapts the interface of the device ({@link FishingBoat}) into {@link RowingBoat}
+ * interface expected by the client ({@link Captain}).
+ */
+public class FishingBoatAdapter implements RowingBoat {
+
+ private final FishingBoat boat = new FishingBoat();
+
+ public final void row() {
+ boat.sail();
+ }
+}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java b/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java
deleted file mode 100644
index 62fab599ff91..000000000000
--- a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.iluwatar.adapter;
-
-/**
- *
- * Adapter class. Adapts the interface of the device (GoblinGlider) into
- * Engineer interface expected by the client (GnomeEngineeringManager).
- *
- */
-public class GnomeEngineer implements Engineer {
-
- private GoblinGlider glider;
-
- public GnomeEngineer() {
- glider = new GoblinGlider();
- }
-
- @Override
- public void operateDevice() {
- glider.attachGlider();
- glider.gainSpeed();
- glider.takeOff();
- }
-
-}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java b/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java
deleted file mode 100644
index 6d0010d74f5b..000000000000
--- a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.iluwatar.adapter;
-
-/**
- *
- * GnomeEngineering manager uses Engineer to operate devices.
- *
- */
-public class GnomeEngineeringManager implements Engineer {
-
- private Engineer engineer;
-
- public GnomeEngineeringManager() {
- engineer = new GnomeEngineer();
- }
-
- @Override
- public void operateDevice() {
- engineer.operateDevice();
- }
-}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java b/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java
deleted file mode 100644
index ff1dbeb8d34e..000000000000
--- a/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.iluwatar.adapter;
-
-/**
- *
- * Device class (adaptee in the pattern).
- *
- */
-public class GoblinGlider {
-
- public void attachGlider() {
- System.out.println("Glider attached.");
- }
-
- public void gainSpeed() {
- System.out.println("Gaining speed.");
- }
-
- public void takeOff() {
- System.out.println("Lift-off!");
- }
-}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
new file mode 100644
index 000000000000..55eeeaf4bd42
--- /dev/null
+++ b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
@@ -0,0 +1,34 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.adapter;
+
+/**
+ * The interface expected by the client.
+ * A rowing boat is rowed to move.
+ */
+public interface RowingBoat {
+
+ void row();
+}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/package-info.java b/adapter/src/main/java/com/iluwatar/adapter/package-info.java
new file mode 100644
index 000000000000..c42f2ac86ceb
--- /dev/null
+++ b/adapter/src/main/java/com/iluwatar/adapter/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.adapter;
diff --git a/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java
new file mode 100644
index 000000000000..bc4984da3d6f
--- /dev/null
+++ b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java
@@ -0,0 +1,74 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.adapter;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/** Tests for the adapter pattern. */
+class AdapterPatternTest {
+
+ private Map beans;
+
+ private static final String FISHING_BEAN = "fisher";
+
+ private static final String ROWING_BEAN = "captain";
+
+ /** This method runs before the test execution and sets the bean objects in the beans Map. */
+ @BeforeEach
+ void setup() {
+ beans = new HashMap<>();
+
+ var fishingBoatAdapter = spy(new FishingBoatAdapter());
+ beans.put(FISHING_BEAN, fishingBoatAdapter);
+
+ var captain = new Captain();
+ captain.setRowingBoat((FishingBoatAdapter) beans.get(FISHING_BEAN));
+ beans.put(ROWING_BEAN, captain);
+ }
+
+ /**
+ * This test asserts that when we use the row() method on a captain bean(client), it is internally
+ * calling sail method on the fishing boat object. The Adapter ({@link FishingBoatAdapter} )
+ * converts the interface of the target class ( {@link FishingBoat}) into a suitable one expected
+ * by the client ({@link Captain} ).
+ */
+ @Test
+ void testAdapter() {
+ var captain = (Captain) beans.get(ROWING_BEAN);
+
+ // when captain moves
+ captain.row();
+
+ // the captain internally calls the battleship object to move
+ var adapter = (RowingBoat) beans.get(FISHING_BEAN);
+ verify(adapter).row();
+ }
+}
diff --git a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
index cd9d713b0356..a2cc4c22bcaa 100644
--- a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
+++ b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
@@ -1,14 +1,40 @@
-package com.iluwatar.adapter;
-
-import org.junit.Test;
-
-import com.iluwatar.adapter.App;
-
-public class AppTest {
-
- @Test
- public void test() {
- String[] args = {};
- App.main(args);
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.adapter;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests that Adapter example runs without errors. */
+class AppTest {
+
+ /** Check whether the execution of the main method in {@link App} throws an exception. */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/ambassador/README.md b/ambassador/README.md
new file mode 100644
index 000000000000..fa08223feb75
--- /dev/null
+++ b/ambassador/README.md
@@ -0,0 +1,236 @@
+---
+title: "Ambassador Pattern in Java: Simplifying Remote Resource Management"
+shortTitle: Ambassador
+description: "Explore the Ambassador Pattern in Java, its benefits, use cases, and practical examples. Learn how to decouple and offload common functionalities to improve system performance and maintainability."
+category: Integration
+language: en
+tag:
+ - API design
+ - Decoupling
+ - Fault tolerance
+ - Proxy
+ - Resilience
+ - Scalability
+---
+
+## Intent of Ambassador Design Pattern
+
+The Ambassador Pattern in Java helps offload common functionalities such as monitoring, logging, and routing from a shared resource to a helper service instance, enhancing performance and maintainability in distributed systems.
+
+## Detailed Explanation of Ambassador Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a busy hotel where guests frequently request restaurant reservations, event tickets, or transportation arrangements. Instead of each guest individually contacting these services, the hotel provides a concierge. The concierge handles these tasks on behalf of the guests, ensuring that reservations are made smoothly, tickets are booked on time, and transportation is scheduled efficiently.
+>
+> In this analogy, the guests are the client services, the external providers (restaurants, ticket vendors, transportation) are the remote services, and the concierge represents the ambassador service. This setup allows the guests to focus on enjoying their stay while the concierge manages the complexities of external interactions, providing a seamless and enhanced experience.
+
+In plain words
+
+> With the Ambassador pattern, we can implement less-frequent polling from clients along with latency checks and logging.
+
+Microsoft documentation states
+
+> An ambassador service can be thought of as an out-of-process proxy which is co-located with the client. This pattern can be useful for offloading common client connectivity tasks such as monitoring, logging, routing, security (such as TLS), and resiliency patterns in a language agnostic way. It is often used with legacy applications, or other applications that are difficult to modify, in order to extend their networking capabilities. It can also enable a specialized team to implement those features.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Ambassador Pattern in Java
+
+In this example of the Ambassador Pattern in Java, we demonstrate how to implement latency checks, logging, and retry mechanisms to improve system reliability.
+
+A remote service has many clients accessing a function it provides. The service is a legacy application and is impossible to update. Large numbers of requests from users are causing connectivity issues. New rules for request frequency should be implemented along with latency checks and client-side logging.
+
+With the above introduction in mind we will imitate the functionality in this example. We have an interface implemented by the remote service as well as the ambassador service.
+
+```java
+interface RemoteServiceInterface {
+ long doRemoteFunction(int value) throws Exception;
+}
+```
+
+A remote services represented as a singleton.
+
+```java
+
+@Slf4j
+public class RemoteService implements RemoteServiceInterface {
+ private static RemoteService service = null;
+
+ static synchronized RemoteService getRemoteService() {
+ if (service == null) {
+ service = new RemoteService();
+ }
+ return service;
+ }
+
+ private RemoteService() {
+ }
+
+ @Override
+ public long doRemoteFunction(int value) {
+ long waitTime = (long) Math.floor(Math.random() * 1000);
+
+ try {
+ sleep(waitTime);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread sleep interrupted", e);
+ }
+
+ return waitTime >= 200 ? value * 10 : -1;
+ }
+}
+```
+
+A service ambassador adds additional features such as logging and latency checks.
+
+```java
+
+@Slf4j
+public class ServiceAmbassador implements RemoteServiceInterface {
+ private static final int RETRIES = 3;
+ private static final int DELAY_MS = 3000;
+
+ ServiceAmbassador() {
+ }
+
+ @Override
+ public long doRemoteFunction(int value) {
+ return safeCall(value);
+ }
+
+ private long checkLatency(int value) {
+ var startTime = System.currentTimeMillis();
+ var result = RemoteService.getRemoteService().doRemoteFunction(value);
+ var timeTaken = System.currentTimeMillis() - startTime;
+
+ LOGGER.info("Time taken (ms): " + timeTaken);
+ return result;
+ }
+
+ private long safeCall(int value) {
+ var retries = 0;
+ var result = (long) FAILURE;
+
+ for (int i = 0; i < RETRIES; i++) {
+ if (retries >= RETRIES) {
+ return FAILURE;
+ }
+
+ if ((result = checkLatency(value)) == FAILURE) {
+ LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
+ retries++;
+ try {
+ sleep(DELAY_MS);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread sleep state interrupted", e);
+ }
+ } else {
+ break;
+ }
+ }
+ return result;
+ }
+}
+```
+
+A client has a local service ambassador used to interact with the remote service.
+
+```java
+
+@Slf4j
+public class Client {
+ private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
+
+ long useService(int value) {
+ var result = serviceAmbassador.doRemoteFunction(value);
+ LOGGER.info("Service result: " + result);
+ return result;
+ }
+}
+```
+
+Here are two clients using the service.
+
+```java
+public class App {
+ public static void main(String[] args) {
+ var host1 = new Client();
+ var host2 = new Client();
+ host1.useService(12);
+ host2.useService(73);
+ }
+}
+```
+
+Here's the output for running the example:
+
+```java
+Time taken(ms):111
+Service result:120
+Time taken(ms):931
+Failed to reach remote:(1)
+Time taken(ms):665
+Failed to reach remote:(2)
+Time taken(ms):538
+Failed to reach remote:(3)
+Service result:-1
+```
+
+## When to Use the Ambassador Pattern in Java
+
+* The Ambassador Pattern is particularly beneficial for Cloud Native and Microservices Architectures in Java. It helps in monitoring, logging, and securing inter-service communication, making it ideal for distributed systems.
+* Legacy System Integration: Facilitates communication with newer services by handling necessary but non-core functionalities.
+* Performance Enhancement: Can be used to cache results or compress data to improve communication efficiency.
+
+Typical use cases include:
+
+* Control access to another object
+* Implement logging
+* Implement circuit breaking
+* Offload remote service tasks
+* Facilitate network connection
+
+## Benefits and Trade-offs of Ambassador Pattern
+
+Benefits:
+
+* Separation of Concerns: Offloads cross-cutting concerns from the service logic, leading to cleaner, more maintainable code.
+* Reusable Infrastructure Logic: The ambassador pattern allows the same logic (e.g., logging, monitoring) to be reused across multiple services.
+* Improved Security: Centralizes security features like SSL termination or authentication, reducing the risk of misconfiguration.
+* Flexibility: Makes it easier to update or replace infrastructure concerns without modifying the service code.
+
+Trade-offs:
+
+* Increased Complexity: Adds another layer to the architecture, which can complicate the system design and debugging.
+* Potential Performance Overhead: The additional network hop can introduce latency and overhead, particularly if not optimized.
+* Deployment Overhead: Requires additional resources and management for deploying and scaling ambassador services.
+
+## Real-World Applications of Ambassador Pattern in Java
+
+* Service Mesh Implementations: In a service mesh architecture, like Istio or Linkerd, the Ambassador pattern is often employed as a sidecar proxy that handles inter-service communications. This includes tasks such as service discovery, routing, load balancing, telemetry (metrics and tracing), and security (authentication and authorization).
+* API Gateways: API gateways can use the Ambassador pattern to encapsulate common functionalities like rate limiting, caching, request shaping, and authentication. This allows backend services to focus on their core business logic without being burdened by these cross-cutting concerns.
+* Logging and Monitoring: An Ambassador can aggregate logs and metrics from various services and forward them to centralized monitoring tools like Prometheus or ELK Stack (Elasticsearch, Logstash, Kibana). This simplifies the logging and monitoring setup for each service and provides a unified view of the system's health.
+* Security: Security-related functionalities such as SSL/TLS termination, identity verification, and encryption can be managed by an Ambassador. This ensures consistent security practices across services and reduces the likelihood of security breaches due to misconfigurations.
+* Resilience: The Ambassador can implement resilience patterns like circuit breakers, retries, and timeouts. For instance, Netflix's Hystrix library can be used within an Ambassador to prevent cascading failures in a microservices ecosystem.
+* Database Proxy: Ambassadors can act as proxies for database connections, providing functionalities like connection pooling, read/write splitting for replicas, and query caching. This offloads significant complexity from the application services.
+* Legacy System Integration: In scenarios where modern microservices need to communicate with legacy systems, an Ambassador can serve as an intermediary that translates protocols, handles necessary transformations, and implements modern security practices, easing the integration process.
+* Network Optimization: For services deployed across different geographical locations or cloud regions, Ambassadors can optimize communication by compressing data, batching requests, or even implementing smart routing to reduce latency and costs.
+* [Kubernetes-native API gateway for microservices](https://github.com/datawire/ambassador)
+
+## Related Java Design Patterns
+
+* [Circuit Breaker](https://java-design-patterns.com/patterns/circuit-breaker/): Often used in conjunction to manage fault tolerance by stopping calls to an unresponsive service.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): The decorator pattern is used to add functionality to an object dynamically, while the ambassador pattern is used to offload functionality to a separate object.
+* [Proxy](https://java-design-patterns.com/patterns/proxy/): Shares similarities with the proxy pattern, but the ambassador pattern specifically focuses on offloading ancillary functionalities.
+* Sidecar: A similar pattern used in the context of containerized applications, where a sidecar container provides additional functionality to the main application container.
+
+## References and Credits
+
+* [Building Microservices: Designing Fine-Grained Systems](https://amzn.to/43aGpSR)
+* [Cloud Native Patterns: Designing Change-tolerant Software](https://amzn.to/3wUAl4O)
+* [Designing Distributed Systems: Patterns and Paradigms for Scalable, Reliable Services](https://amzn.to/3T9g9Uj)
+* [Microservices Patterns: With examples in Java](https://amzn.to/3UyWD5O)
+* [Ambassador pattern (Microsoft)](https://docs.microsoft.com/en-us/azure/architecture/patterns/ambassador)
diff --git a/ambassador/etc/ambassador-sequence-diagram.png b/ambassador/etc/ambassador-sequence-diagram.png
new file mode 100644
index 000000000000..71e6a947c5dd
Binary files /dev/null and b/ambassador/etc/ambassador-sequence-diagram.png differ
diff --git a/ambassador/etc/ambassador.urm.png b/ambassador/etc/ambassador.urm.png
new file mode 100644
index 000000000000..9b50a02ad356
Binary files /dev/null and b/ambassador/etc/ambassador.urm.png differ
diff --git a/ambassador/etc/ambassador.urm.puml b/ambassador/etc/ambassador.urm.puml
new file mode 100644
index 000000000000..517b0bf51d07
--- /dev/null
+++ b/ambassador/etc/ambassador.urm.puml
@@ -0,0 +1,47 @@
+@startuml
+package com.iluwatar.ambassador.util {
+ interface RandomProvider {
+ + random() : double {abstract}
+ }
+}
+package com.iluwatar.ambassador {
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ class Client {
+ - LOGGER : Logger {static}
+ - serviceAmbassador : ServiceAmbassador
+ + Client()
+ ~ useService(value : int) : long
+ }
+ class RemoteService {
+ - LOGGER : Logger {static}
+ - THRESHOLD : int {static}
+ - randomProvider : RandomProvider
+ - service : RemoteService {static}
+ - RemoteService()
+ ~ RemoteService(randomProvider : RandomProvider)
+ + doRemoteFunction(value : int) : long
+ ~ getRemoteService() : RemoteService {static}
+ }
+ ~interface RemoteServiceInterface {
+ + FAILURE : int {static}
+ + doRemoteFunction(int) : long {abstract}
+ }
+ class ServiceAmbassador {
+ - DELAY_MS : int {static}
+ - LOGGER : Logger {static}
+ - RETRIES : int {static}
+ ~ ServiceAmbassador()
+ - checkLatency(value : int) : long
+ + doRemoteFunction(value : int) : long
+ - safeCall(value : int) : long
+ }
+}
+RemoteService --> "-service" RemoteService
+Client --> "-serviceAmbassador" ServiceAmbassador
+RemoteService --> "-randomProvider" RandomProvider
+RemoteService ..|> RemoteServiceInterface
+ServiceAmbassador ..|> RemoteServiceInterface
+@enduml
\ No newline at end of file
diff --git a/ambassador/pom.xml b/ambassador/pom.xml
new file mode 100644
index 000000000000..15e4a07f0dc3
--- /dev/null
+++ b/ambassador/pom.xml
@@ -0,0 +1,70 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ ambassador
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.ambassador.App
+
+
+
+
+
+
+
+
+
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/App.java b/ambassador/src/main/java/com/iluwatar/ambassador/App.java
new file mode 100644
index 000000000000..8de149fe0813
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/App.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador;
+
+/**
+ * The ambassador pattern creates a helper service that sends network requests on behalf of a
+ * client. It is often used in cloud-based applications to offload features of a remote service.
+ *
+ * An ambassador service can be thought of as an out-of-process proxy that is co-located with the
+ * client. Similar to the proxy design pattern, the ambassador service provides an interface for
+ * another remote service. In addition to the interface, the ambassador provides extra functionality
+ * and features, specifically offloaded common connectivity tasks. This usually consists of
+ * monitoring, logging, routing, security etc. This is extremely useful in legacy applications where
+ * the codebase is difficult to modify and allows for improvements in the application's networking
+ * capabilities.
+ *
+ *
In this example, we will the ({@link ServiceAmbassador}) class represents the ambassador while
+ * the ({@link RemoteService}) class represents a remote application.
+ */
+public class App {
+
+ /** Entry point. */
+ public static void main(String[] args) {
+ var host1 = new Client();
+ var host2 = new Client();
+ host1.useService(12);
+ host2.useService(73);
+ }
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/Client.java b/ambassador/src/main/java/com/iluwatar/ambassador/Client.java
new file mode 100644
index 000000000000..0baabf4ffc06
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/Client.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador;
+
+import lombok.extern.slf4j.Slf4j;
+
+/** A simple Client. */
+@Slf4j
+public class Client {
+
+ private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
+
+ long useService(int value) {
+ var result = serviceAmbassador.doRemoteFunction(value);
+ LOGGER.info("Service result: {}", result);
+ return result;
+ }
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java
new file mode 100644
index 000000000000..d99348040cfe
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java
@@ -0,0 +1,78 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador;
+
+import static java.lang.Thread.sleep;
+
+import com.iluwatar.ambassador.util.RandomProvider;
+import lombok.extern.slf4j.Slf4j;
+
+/** A remote legacy application represented by a Singleton implementation. */
+@Slf4j
+public class RemoteService implements RemoteServiceInterface {
+ private static final int THRESHOLD = 200;
+ private static RemoteService service = null;
+ private final RandomProvider randomProvider;
+
+ static synchronized RemoteService getRemoteService() {
+ if (service == null) {
+ service = new RemoteService();
+ }
+ return service;
+ }
+
+ private RemoteService() {
+ this(Math::random);
+ }
+
+ /** This constructor is used for testing purposes only. */
+ RemoteService(RandomProvider randomProvider) {
+ this.randomProvider = randomProvider;
+ }
+
+ /**
+ * Remote function takes a value and multiplies it by 10 taking a random amount of time. Will
+ * sometimes return -1. This imitates connectivity issues a client might have to account for.
+ *
+ * @param value integer value to be multiplied.
+ * @return if waitTime is less than {@link RemoteService#THRESHOLD}, it returns value * 10,
+ * otherwise {@link RemoteServiceStatus#FAILURE}.
+ */
+ @Override
+ public long doRemoteFunction(int value) {
+
+ long waitTime = (long) Math.floor(randomProvider.random() * 1000);
+
+ try {
+ sleep(waitTime);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread sleep state interrupted", e);
+ Thread.currentThread().interrupt();
+ }
+ return waitTime <= THRESHOLD
+ ? value * 10
+ : RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue();
+ }
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java
new file mode 100644
index 000000000000..aa6012bae33f
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java
@@ -0,0 +1,31 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador;
+
+/** Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}). */
+interface RemoteServiceInterface {
+
+ long doRemoteFunction(int value);
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java
new file mode 100644
index 000000000000..8549ed7247f3
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java
@@ -0,0 +1,44 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador;
+
+import lombok.Getter;
+
+/**
+ * Holds information regarding the status of the Remote Service.
+ *
+ *
This Enum replaces the integer value previously stored in {@link RemoteServiceInterface} as
+ * SonarCloud was identifying it as an issue. All test cases have been checked after changes,
+ * without failures.
+ */
+public enum RemoteServiceStatus {
+ FAILURE(-1);
+
+ @Getter private final long remoteServiceStatusValue;
+
+ RemoteServiceStatus(long remoteServiceStatusValue) {
+ this.remoteServiceStatusValue = remoteServiceStatusValue;
+ }
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java b/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java
new file mode 100644
index 000000000000..4d310169770b
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java
@@ -0,0 +1,83 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador;
+
+import static com.iluwatar.ambassador.RemoteServiceStatus.FAILURE;
+import static java.lang.Thread.sleep;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * ServiceAmbassador provides an interface for a ({@link Client}) to access ({@link RemoteService}).
+ * The interface adds logging, latency testing and usage of the service in a safe way that will not
+ * add stress to the remote service when connectivity issues occur.
+ */
+@Slf4j
+public class ServiceAmbassador implements RemoteServiceInterface {
+
+ private static final int RETRIES = 3;
+ private static final int DELAY_MS = 3000;
+
+ ServiceAmbassador() {}
+
+ @Override
+ public long doRemoteFunction(int value) {
+ return safeCall(value);
+ }
+
+ private long checkLatency(int value) {
+ var startTime = System.currentTimeMillis();
+ var result = RemoteService.getRemoteService().doRemoteFunction(value);
+ var timeTaken = System.currentTimeMillis() - startTime;
+
+ LOGGER.info("Time taken (ms): {}", timeTaken);
+ return result;
+ }
+
+ private long safeCall(int value) {
+ var retries = 0;
+ var result = FAILURE.getRemoteServiceStatusValue();
+
+ for (int i = 0; i < RETRIES; i++) {
+ if (retries >= RETRIES) {
+ return FAILURE.getRemoteServiceStatusValue();
+ }
+
+ if ((result = checkLatency(value)) == FAILURE.getRemoteServiceStatusValue()) {
+ LOGGER.info("Failed to reach remote: ({})", i + 1);
+ retries++;
+ try {
+ sleep(DELAY_MS);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread sleep state interrupted", e);
+ Thread.currentThread().interrupt();
+ }
+ } else {
+ break;
+ }
+ }
+ return result;
+ }
+}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java b/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java
new file mode 100644
index 000000000000..4eba2fada7fa
--- /dev/null
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java
@@ -0,0 +1,30 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador.util;
+
+/** An interface for randomness. Useful for testing purposes. */
+public interface RandomProvider {
+ double random();
+}
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java
new file mode 100644
index 000000000000..ddb2d6eff411
--- /dev/null
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java
@@ -0,0 +1,45 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Application 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} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java
new file mode 100644
index 000000000000..24603efff9d4
--- /dev/null
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java
@@ -0,0 +1,42 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link Client} */
+class ClientTest {
+
+ @Test
+ void test() {
+ Client client = new Client();
+ var result = client.useService(10);
+
+ assertTrue(
+ result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
+ }
+}
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java
new file mode 100644
index 000000000000..81e4f744128f
--- /dev/null
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java
@@ -0,0 +1,61 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.iluwatar.ambassador.util.RandomProvider;
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link RemoteService} */
+class RemoteServiceTest {
+
+ @Test
+ void testFailedCall() {
+ var remoteService = new RemoteService(new StaticRandomProvider(0.21));
+ var result = remoteService.doRemoteFunction(10);
+ assertEquals(RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue(), result);
+ }
+
+ @Test
+ void testSuccessfulCall() {
+ var remoteService = new RemoteService(new StaticRandomProvider(0.2));
+ var result = remoteService.doRemoteFunction(10);
+ assertEquals(100, result);
+ }
+
+ private static class StaticRandomProvider implements RandomProvider {
+ private final double value;
+
+ StaticRandomProvider(double value) {
+ this.value = value;
+ }
+
+ @Override
+ public double random() {
+ return value;
+ }
+ }
+}
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
new file mode 100644
index 000000000000..0543b2e7e370
--- /dev/null
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.ambassador;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link ServiceAmbassador} */
+class ServiceAmbassadorTest {
+
+ @Test
+ void test() {
+ long result = new ServiceAmbassador().doRemoteFunction(10);
+ assertTrue(
+ result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
+ }
+}
diff --git a/anti-corruption-layer/README.md b/anti-corruption-layer/README.md
new file mode 100644
index 000000000000..9b6042850eba
--- /dev/null
+++ b/anti-corruption-layer/README.md
@@ -0,0 +1,185 @@
+---
+title: "Anti-Corruption Layer Pattern in Java: Ensuring System Integrity Amidst Legacy Systems"
+shortTitle: Anti-Corruption Layer
+description: "Learn how the Anti-Corruption Layer design pattern helps in decoupling subsystems, preventing data corruption, and facilitating seamless integration in Java applications."
+category: Integration
+language: en
+tag:
+ - Architecture
+ - Decoupling
+ - Integration
+ - Isolation
+ - Layered architecture
+ - Migration
+ - Modernization
+ - Refactoring
+ - Wrapping
+---
+
+## Also known as
+
+* ACL
+* Interface layer
+* Translation layer
+
+## Intent of Anti-Corruption Layer Design Pattern
+
+The Anti-Corruption Layer (ACL) is a crucial design pattern in Java development, particularly for system integration and maintaining data integrity. Implement a façade or adapter layer between different subsystems that don't share the same semantics. It translates between different data formats and systems, ensuring that the integration between systems does not lead to corruption of business logic or data integrity.
+
+## Detailed Explanation of Anti-Corruption Layer Pattern with Real-World Examples
+
+Real-world example
+
+> This example demonstrates how the Anti-Corruption Layer ensures seamless integration between legacy systems and modern platforms, crucial for maintaining business logic integrity during system migration.
+>
+> Imagine a large retail company transitioning its inventory management system from an old legacy software to a new modern platform. The legacy system has been in use for decades and contains complex business rules and data formats that are incompatible with the new system. Instead of directly connecting the new system to the legacy one, the company implements an Anti-Corruption Layer (ACL).
+>
+> The ACL acts as a mediator, translating and adapting data between the two systems. When the new system requests inventory data, the ACL translates the request into a format the legacy system understands, retrieves the data, and then translates it back into a format suitable for the new system. This approach ensures that the new system remains unaffected by the intricacies of the legacy system, preventing corruption of data and business logic while facilitating a smooth transition.
+
+In plain words
+
+> The Anti-Corruption Layer design pattern protects a system from the complexities and changes of external systems by providing an intermediary translation layer.
+
+[Microsoft's documentation](https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer) says
+
+> Implement a façade or adapter layer between different subsystems that don't share the same semantics. This layer translates requests that one subsystem makes to the other subsystem. Use this pattern to ensure that an application's design is not limited by dependencies on outside subsystems. This pattern was first described by Eric Evans in Domain-Driven Design.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Anti-Corruption Layer Pattern in Java
+
+The ACL design pattern in Java provides an intermediary layer that translates data formats, ensuring that integration between different systems does not lead to data corruption.
+
+Here are 2 shop-ordering systems: `Legacy` and `Modern`.
+
+The aforementioned systems have different domain models and have to operate simultaneously. Since they work independently the orders can come either from the `Legacy` or `Modern` system. Therefore, the system that receives the legacyOrder needs to check if the legacyOrder is valid and not present in the other system. Then it can place the legacyOrder in its own system.
+
+But for that, the system needs to know the domain model of the other system and to avoid that, the anti-corruption layer(ACL) is introduced. The ACL is a layer that translates the domain model of the `Legacy` system to the domain model of the `Modern` system and vice versa. Also, it hides all other operations with the other system, uncoupling the systems.
+
+Domain model of the `Legacy` system:
+
+```java
+public class LegacyOrder {
+ private String id;
+ private String customer;
+ private String item;
+ private String qty;
+ private String price;
+}
+```
+
+Domain model of the `Modern` system:
+
+```java
+public class ModernOrder {
+ private String id;
+ private Customer customer;
+
+ private Shipment shipment;
+
+ private String extra;
+}
+
+public class Customer {
+ private String address;
+}
+
+public class Shipment {
+ private String item;
+ private String qty;
+ private String price;
+}
+```
+
+Anti-corruption layer:
+
+```java
+public class AntiCorruptionLayer {
+
+ @Autowired
+ private ModernShop modernShop;
+
+ @Autowired
+ private LegacyShop legacyShop;
+
+ public Optional findOrderInModernSystem(String id) {
+ return modernShop.findOrder(id).map(o -> /* map to legacyOrder*/);
+ }
+
+ public Optional findOrderInLegacySystem(String id) {
+ return legacyShop.findOrder(id).map(o -> /* map to modernOrder*/);
+ }
+
+}
+```
+
+The connection between the systems. Wherever the `Legacy` or `Modern` system needs to communicate with the counterpart the ACL needs to be used to avoid corrupting the current domain model. The example below shows how the `Legacy` system places an order with a validation from the `Modern` system.
+
+```java
+public class LegacyShop {
+ @Autowired
+ private AntiCorruptionLayer acl;
+
+ public void placeOrder(LegacyOrder legacyOrder) throws ShopException {
+
+ String id = legacyOrder.getId();
+
+ Optional orderInModernSystem = acl.findOrderInModernSystem(id);
+
+ if (orderInModernSystem.isPresent()) {
+ // order is already in the modern system
+ } else {
+ // place order in the current system
+ }
+ }
+}
+```
+
+## When to Use the Anti-Corruption Layer Pattern in Java
+
+Use this pattern when:
+
+* A migration is planned to happen over multiple stages, but integration between new and legacy systems needs to be maintained
+* Two or more subsystems have different semantics, but still need to communicate
+* When integrating with legacy systems or external systems where direct integration might pollute the domain model of the new system
+* In scenarios where different subsystems within a larger system use different data formats or structures
+* When there is a need to ensure loose coupling between different subsystems or external services to facilitate easier maintenance and scalability
+
+## Anti-Corruption Layer Pattern Java Tutorials
+
+* [Anti-Corruption Layer (Microsoft)](https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer)
+* [Anti-Corruption Layer Pattern (Amazon)](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/acl.html)
+
+## Real-World Applications of Anti-Corruption Layer Pattern in Java
+
+* Microservices architectures where individual services must communicate without being tightly coupled to each other’s data schemas
+* Enterprise systems integration, especially when integrating modern systems with legacy systems
+* In bounded contexts within Domain-Driven Design (DDD) to maintain the integrity of a domain model when interacting with external systems or subsystems
+
+## Benefits and Trade-offs of Anti-Corruption Layer Pattern
+
+Benefits:
+
+* Protects the integrity of the domain model by providing a clear boundary
+* Promotes loose coupling between systems, making the system more resilient to changes in external systems
+* Facilitates cleaner and more maintainable code by isolating integration code from business logic
+
+Trade-offs:
+
+* Introduces additional complexity and potential performance overhead due to the translation process
+* Requires extra effort in design and implementation to ensure the layer is effective without becoming a bottleneck
+* Can lead to duplication of models if not carefully managed
+
+## Related Java Design Patterns
+
+* [Adapter](https://java-design-patterns.com/patterns/adapter/): The Anti-Corruption Layer can be implemented using the Adapter pattern to translate between different data formats or structures
+* [Facade](https://java-design-patterns.com/patterns/facade/): The Anti-Corruption Layer can be seen as a specialized form of the Facade pattern that is used to isolate different subsystems
+* [Gateway](https://java-design-patterns.com/patterns/gateway/): The Anti-Corruption Layer can be used as a Gateway to external systems to provide a unified interface
+
+## References and Credits
+
+* [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://amzn.to/3vptcJz)
+* [Implementing Domain-Driven Design](https://amzn.to/3ISOSRA)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
diff --git a/anti-corruption-layer/etc/anti-corruption-layer-sequence-diagram.png b/anti-corruption-layer/etc/anti-corruption-layer-sequence-diagram.png
new file mode 100644
index 000000000000..835ff4e84a0f
Binary files /dev/null and b/anti-corruption-layer/etc/anti-corruption-layer-sequence-diagram.png differ
diff --git a/anti-corruption-layer/etc/anti-corruption-layer.urm.png b/anti-corruption-layer/etc/anti-corruption-layer.urm.png
new file mode 100644
index 000000000000..96e432121e7e
Binary files /dev/null and b/anti-corruption-layer/etc/anti-corruption-layer.urm.png differ
diff --git a/anti-corruption-layer/etc/anti-corruption-layer.urm.puml b/anti-corruption-layer/etc/anti-corruption-layer.urm.puml
new file mode 100644
index 000000000000..6b1d1cdb9b81
--- /dev/null
+++ b/anti-corruption-layer/etc/anti-corruption-layer.urm.puml
@@ -0,0 +1,25 @@
+@startuml
+package com.iluwatar.corruption {
+class LegacyShop {
+ private Store store;
+ private AntiCorruptionLayer acl;
+}
+
+class ModernShop {
+ private Store store;
+ private AntiCorruptionLayer acl;
+}
+
+class AntiCorruptionLayer{
+ private LegacyShop legacyShop;
+ private ModernShop modernShop;
+
+
+ }
+LegacyShop ---> "findOrderInModernSystem" AntiCorruptionLayer
+ModernShop ---> "findOrderInLegacySystem" AntiCorruptionLayer
+AntiCorruptionLayer ..|> ModernShop
+AntiCorruptionLayer ..|> LegacyShop
+}
+
+@enduml
\ No newline at end of file
diff --git a/anti-corruption-layer/pom.xml b/anti-corruption-layer/pom.xml
new file mode 100644
index 000000000000..2fddfd3ecc46
--- /dev/null
+++ b/anti-corruption-layer/pom.xml
@@ -0,0 +1,72 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ anti-corruption-layer
+ jar
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.corruption.App
+
+
+
+
+
+
+
+
+
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java
new file mode 100644
index 000000000000..f7cf8f075f83
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * This layer translates communications between the two systems, allowing one system to remain
+ * unchanged while the other can avoid compromising its design and technological approach.
+ */
+@SpringBootApplication
+public class App {
+
+ public static void main(String[] args) {
+ SpringApplication.run(App.class, args);
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java
new file mode 100644
index 000000000000..880d98c7d5b0
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java
@@ -0,0 +1,48 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/**
+ * Context and problem Most applications rely on other systems for some data or functionality. For
+ * example, when a legacy application is migrated to a modern system, it may still need existing
+ * legacy resources. New features must be able to call the legacy system. This is especially true of
+ * gradual migrations, where different features of a larger application are moved to a modern system
+ * over time.
+ *
+ * Often these legacy systems suffer from quality issues such as convoluted data schemas or
+ * obsolete APIs. The features and technologies used in legacy systems can vary widely from more
+ * modern systems. To interoperate with the legacy system, the new application may need to support
+ * outdated infrastructure, protocols, data models, APIs, or other features that you wouldn't
+ * otherwise put into a modern application.
+ *
+ *
Maintaining access between new and legacy systems can force the new system to adhere to at
+ * least some of the legacy system's APIs or other semantics. When these legacy features have
+ * quality issues, supporting them "corrupts" what might otherwise be a cleanly designed modern
+ * application. Similar issues can arise with any external system that your development team doesn't
+ * control, not just legacy systems.
+ *
+ *
Solution Isolate the different subsystems by placing an anti-corruption layer between them.
+ * This layer translates communications between the two systems, allowing one system to remain
+ * unchanged while the other can avoid compromising its design and technological approach.
+ */
+package com.iluwatar.corruption;
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java
new file mode 100644
index 000000000000..4e8a17fa5d2a
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java
@@ -0,0 +1,66 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system;
+
+import com.iluwatar.corruption.system.legacy.LegacyShop;
+import com.iluwatar.corruption.system.modern.Customer;
+import com.iluwatar.corruption.system.modern.ModernOrder;
+import com.iluwatar.corruption.system.modern.Shipment;
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * The class represents an anti-corruption layer. The main purpose of the class is to provide a
+ * layer between the modern and legacy systems. The class is responsible for converting the data
+ * from one system to another decoupling the systems to each other
+ *
+ *
It allows using one system a domain model of the other system without changing the domain
+ * model of the system.
+ */
+@Service
+public class AntiCorruptionLayer {
+
+ @Autowired private LegacyShop legacyShop;
+
+ /**
+ * The method converts the order from the legacy system to the modern system.
+ *
+ * @param id the id of the order
+ * @return the order in the modern system
+ */
+ public Optional findOrderInLegacySystem(String id) {
+
+ return legacyShop
+ .findOrder(id)
+ .map(
+ o ->
+ new ModernOrder(
+ o.getId(),
+ new Customer(o.getCustomer()),
+ new Shipment(o.getItem(), o.getQty(), o.getPrice()),
+ ""));
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java
new file mode 100644
index 000000000000..e84578528be7
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java
@@ -0,0 +1,49 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system;
+
+import java.util.HashMap;
+import java.util.Optional;
+
+/**
+ * The class represents a data store for the modern system.
+ *
+ * @param the type of the value stored in the data store
+ */
+public abstract class DataStore {
+ private final HashMap inner;
+
+ public DataStore() {
+ inner = new HashMap<>();
+ }
+
+ public Optional get(String key) {
+ return Optional.ofNullable(inner.get(key));
+ }
+
+ public Optional put(String key, V value) {
+ return Optional.ofNullable(inner.put(key, value));
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java
new file mode 100644
index 000000000000..c0acd288ed0c
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java
@@ -0,0 +1,50 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system;
+
+/** The class represents a general exception for the shop. */
+public class ShopException extends Exception {
+ public ShopException(String message) {
+ super(message);
+ }
+
+ /**
+ * Throws an exception that the order is already placed but has an incorrect data.
+ *
+ * @param lhs the incoming order
+ * @param rhs the existing order
+ * @return the exception
+ * @throws ShopException the exception
+ */
+ public static ShopException throwIncorrectData(String lhs, String rhs) throws ShopException {
+ throw new ShopException(
+ "The order is already placed but has an incorrect data:\n"
+ + "Incoming order: "
+ + lhs
+ + "\n"
+ + "Existing order: "
+ + rhs);
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java
new file mode 100644
index 000000000000..45faa06cb26c
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java
@@ -0,0 +1,43 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system.legacy;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * The class represents an order in the legacy system. The class is used by the legacy system to
+ * store the data.
+ */
+@Data
+@AllArgsConstructor
+public class LegacyOrder {
+ private String id;
+ private String customer;
+
+ private String item;
+ private int qty;
+ private int price;
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java
new file mode 100644
index 000000000000..b74eb1c29718
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java
@@ -0,0 +1,52 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system.legacy;
+
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * The class represents a legacy shop system. The main purpose is to place the order from the
+ * customers.
+ */
+@Service
+public class LegacyShop {
+ @Autowired private LegacyStore store;
+
+ /**
+ * Places the order in the legacy system. If the order is already present in the modern system,
+ * then the order is placed only if the data is the same. If the order is not present in the
+ * modern system, then the order is placed in the legacy system.
+ */
+ public void placeOrder(LegacyOrder legacyOrder) {
+ store.put(legacyOrder.getId(), legacyOrder);
+ }
+
+ /** Finds the order in the legacy system. */
+ public Optional findOrder(String orderId) {
+ return store.get(orderId);
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java
new file mode 100644
index 000000000000..ec1d613a7235
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java
@@ -0,0 +1,35 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system.legacy;
+
+import com.iluwatar.corruption.system.DataStore;
+import org.springframework.stereotype.Service;
+
+/**
+ * The class represents a data store for the legacy system. The class is used by the legacy system
+ * to store the data.
+ */
+@Service
+public class LegacyStore extends DataStore {}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java
new file mode 100644
index 000000000000..130f36d39674
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java
@@ -0,0 +1,35 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system.modern;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/** The class represents a customer in the modern system. */
+@Data
+@AllArgsConstructor
+public class Customer {
+ private String address;
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java
new file mode 100644
index 000000000000..7b62985015d6
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system.modern;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/** The class represents an order in the modern system. */
+@Data
+@AllArgsConstructor
+public class ModernOrder {
+ private String id;
+ private Customer customer;
+
+ private Shipment shipment;
+
+ private String extra;
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java
new file mode 100644
index 000000000000..24080abe1533
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java
@@ -0,0 +1,67 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system.modern;
+
+import com.iluwatar.corruption.system.AntiCorruptionLayer;
+import com.iluwatar.corruption.system.ShopException;
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * The class represents a modern shop system. The main purpose of the class is to place orders and
+ * find orders.
+ */
+@Service
+public class ModernShop {
+ @Autowired private ModernStore store;
+
+ @Autowired private AntiCorruptionLayer acl;
+
+ /**
+ * Places the order in the modern system. If the order is already present in the legacy system,
+ * then no need to place it again.
+ */
+ public void placeOrder(ModernOrder order) throws ShopException {
+
+ String id = order.getId();
+ // check if the order is already present in the legacy system
+ Optional orderInObsoleteSystem = acl.findOrderInLegacySystem(id);
+
+ if (orderInObsoleteSystem.isPresent()) {
+ var legacyOrder = orderInObsoleteSystem.get();
+ if (!order.equals(legacyOrder)) {
+ throw ShopException.throwIncorrectData(legacyOrder.toString(), order.toString());
+ }
+ } else {
+ store.put(id, order);
+ }
+ }
+
+ /** Finds the order in the modern system. */
+ public Optional findOrder(String orderId) {
+ return store.get(orderId);
+ }
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java
new file mode 100644
index 000000000000..4fb3952fae5e
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java
@@ -0,0 +1,32 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system.modern;
+
+import com.iluwatar.corruption.system.DataStore;
+import org.springframework.stereotype.Service;
+
+/** The class represents a data store for the modern system. */
+@Service
+public class ModernStore extends DataStore {}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java
new file mode 100644
index 000000000000..085a3921ceeb
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java
@@ -0,0 +1,40 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system.modern;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * The class represents a shipment in the modern system. The class is used by the modern system to
+ * store the data.
+ */
+@Data
+@AllArgsConstructor
+public class Shipment {
+ private String item;
+ private int qty;
+ private int price;
+}
diff --git a/anti-corruption-layer/src/main/resources/application.properties b/anti-corruption-layer/src/main/resources/application.properties
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java b/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java
new file mode 100644
index 000000000000..ee46d124eee6
--- /dev/null
+++ b/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java
@@ -0,0 +1,96 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.corruption.system;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.iluwatar.corruption.system.legacy.LegacyOrder;
+import com.iluwatar.corruption.system.legacy.LegacyShop;
+import com.iluwatar.corruption.system.modern.Customer;
+import com.iluwatar.corruption.system.modern.ModernOrder;
+import com.iluwatar.corruption.system.modern.ModernShop;
+import com.iluwatar.corruption.system.modern.Shipment;
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest
+public class AntiCorruptionLayerTest {
+
+ @Autowired private LegacyShop legacyShop;
+
+ @Autowired private ModernShop modernShop;
+
+ /**
+ * Test the anti-corruption layer. Main intention is to demonstrate how the anti-corruption layer
+ * works. The 2 shops (modern and legacy) should operate independently and in the same time
+ * synchronize the data.
+ */
+ @Test
+ public void antiCorruptionLayerTest() throws ShopException {
+ // a new order comes to the legacy shop.
+ LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
+ // place the order in the legacy shop.
+ legacyShop.placeOrder(legacyOrder);
+ // the order is placed as usual since there is no other orders with the id in the both systems.
+ Optional legacyOrderWithIdOne = legacyShop.findOrder("1");
+ assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
+
+ // a new order (or maybe just the same order) appears in the modern shop
+ ModernOrder modernOrder =
+ new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 1, 1), "");
+ // the system places it, but it checks if there is an order with the same id in the legacy shop.
+ modernShop.placeOrder(modernOrder);
+
+ Optional modernOrderWithIdOne = modernShop.findOrder("1");
+ // there is no new order placed since there is already an order with the same id in the legacy
+ // shop.
+ assertTrue(modernOrderWithIdOne.isEmpty());
+ }
+
+ /**
+ * Test the anti-corruption layer when a conflict occurs between systems. This test ensures that
+ * an exception is thrown when conflicting orders are placed.
+ */
+ @Test
+ public void antiCorruptionLayerWithExTest() throws ShopException {
+ // a new order comes to the legacy shop.
+ LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
+ // place the order in the legacy shop.
+ legacyShop.placeOrder(legacyOrder);
+ // the order is placed as usual since there is no other orders with the id in the both systems.
+ Optional legacyOrderWithIdOne = legacyShop.findOrder("1");
+ assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
+ // a new order but with the same id and different data appears in the modern shop
+ ModernOrder modernOrder =
+ new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 10, 1), "");
+ // the system rejects the order since there are 2 orders with contradiction there.
+ assertThrows(ShopException.class, () -> modernShop.placeOrder(modernOrder));
+ }
+}
diff --git a/arrange-act-assert/README.md b/arrange-act-assert/README.md
new file mode 100644
index 000000000000..584d8601a23e
--- /dev/null
+++ b/arrange-act-assert/README.md
@@ -0,0 +1,167 @@
+---
+title: "Arrange/Act/Assert Pattern in Java: Enhance Testing Clarity and Simplicity"
+shortTitle: Arrange/Act/Assert
+description: "Learn how to use the Arrange/Act/Assert pattern to structure your unit tests in Java. Improve readability and maintainability of your code with clear testing phases."
+category: Testing
+language: en
+tag:
+ - Code simplification
+ - Isolation
+ - Testing
+---
+
+## Also known as
+
+* Given/When/Then
+
+## Intent of Arrange/Act/Assert Design Pattern
+
+The Arrange/Act/Assert pattern is essential in unit testing in Java. This testing method structures unit tests clearly by dividing them into three distinct sections: setup (Arrange), execution (Act), and verification (Assert).
+
+## Detailed Explanation of Arrange/Act/Assert Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine you are organizing a small event. To ensure everything runs smoothly, you follow a pattern similar to Arrange/Act/Assert:
+>
+> 1. **Arrange**: You set up the venue, prepare the guest list, arrange seating, and organize the catering.
+> 2. **Act**: You conduct the event according to the plan, welcoming guests, serving food, and following the schedule.
+> 3. **Assert**: After the event, you evaluate its success by checking guest feedback, ensuring all tasks were completed, and reviewing if everything went as planned.
+>
+> This clear separation of preparation, execution, and evaluation helps ensure the event is well-organized and successful, mirroring the structured approach of the Arrange/Act/Assert pattern in software testing.
+
+In plain words
+
+> Arrange/Act/Assert is a testing pattern that organizes tests into three clear steps for easy maintenance.
+
+WikiWikiWeb says
+
+> Arrange/Act/Assert is a pattern for arranging and formatting code in UnitTest methods.
+
+Flowchart
+
+
+
+## Programmatic Example of Arrange/Act/Assert Pattern in Java
+
+We need to write comprehensive and clear unit test suite for a class. Using the Arrange/Act/Assert pattern in Java testing ensures clarity.
+
+Let's first introduce our `Cash` class to be unit tested.
+
+```java
+public class Cash {
+
+ private int amount;
+
+ Cash(int amount) {
+ this.amount = amount;
+ }
+
+ void plus(int addend) {
+ amount += addend;
+ }
+
+ boolean minus(int subtrahend) {
+ if (amount >= subtrahend) {
+ amount -= subtrahend;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ int count() {
+ return amount;
+ }
+}
+```
+
+Then we write our unit tests according to Arrange/Act/Assert pattern. Notice the clearly separated steps for each unit test.
+
+```java
+class CashAAATest {
+
+ @Test
+ void testPlus() {
+ //Arrange
+ var cash = new Cash(3);
+ //Act
+ cash.plus(4);
+ //Assert
+ assertEquals(7, cash.count());
+ }
+
+ @Test
+ void testMinus() {
+ //Arrange
+ var cash = new Cash(8);
+ //Act
+ var result = cash.minus(5);
+ //Assert
+ assertTrue(result);
+ assertEquals(3, cash.count());
+ }
+
+ @Test
+ void testInsufficientMinus() {
+ //Arrange
+ var cash = new Cash(1);
+ //Act
+ var result = cash.minus(6);
+ //Assert
+ assertFalse(result);
+ assertEquals(1, cash.count());
+ }
+
+ @Test
+ void testUpdate() {
+ //Arrange
+ var cash = new Cash(5);
+ //Act
+ cash.plus(6);
+ var result = cash.minus(3);
+ //Assert
+ assertTrue(result);
+ assertEquals(8, cash.count());
+ }
+}
+```
+
+## When to Use the Arrange/Act/Assert Pattern in Java
+
+Use Arrange/Act/Assert pattern when
+
+* Unit testing, especially within the context of TDD and BDD
+* Anywhere clarity and structure are needed in test cases
+
+## Real-World Applications of Arrange/Act/Assert Pattern in Java
+
+* This pattern is particularly useful when practicing TDD and/or BDD methodologies in Java.
+* Utilized in various programming languages and testing frameworks, such as JUnit (Java), NUnit (.NET), and xUnit frameworks.
+
+## Benefits and Trade-offs of Arrange/Act/Assert Pattern
+
+Benefits:
+
+* Improved readability of tests by clearly separating the setup, action, and verification steps.
+* Easier maintenance and understanding of tests, as each test is structured in a predictable way.
+* Facilitates debugging by isolating test failures to specific phases within the test.
+
+Trade-offs:
+
+* May introduce redundancy in tests, as similar arrangements may be repeated across tests.
+* Some complex tests might not fit neatly into this structure, requiring additional context or setup outside these three phases.
+
+## Related Java Design Patterns
+
+* [Page Object](https://java-design-patterns.com/patterns/page-object/): A pattern for organizing UI tests that can be used in conjunction with Arrange/Act/Assert.
+
+## References and Credits
+
+* [The Art of Unit Testing: with examples in C#](https://amzn.to/49IbdwO)
+* [Test Driven Development: By Example](https://amzn.to/3wEwKbF)
+* [Unit Testing Principles, Practices, and Patterns: Effective testing styles, patterns, and reliable automation for unit testing, mocking, and integration testing with examples in C#](https://amzn.to/4ayjpiM)
+* [xUnit Test Patterns: Refactoring Test Code](https://amzn.to/4dHGDpm)
+* [Arrange, Act, Assert: What is AAA Testing?](https://blog.ncrunch.net/post/arrange-act-assert-aaa-testing.aspx)
+* [Bill Wake: 3A – Arrange, Act, Assert (NCrunch)](https://xp123.com/articles/3a-arrange-act-assert/)
+* [GivenWhenThen (Martin Fowler)](https://martinfowler.com/bliki/GivenWhenThen.html)
diff --git a/arrange-act-assert/etc/arrange-act-assert-flowchart.png b/arrange-act-assert/etc/arrange-act-assert-flowchart.png
new file mode 100644
index 000000000000..8b7615352523
Binary files /dev/null and b/arrange-act-assert/etc/arrange-act-assert-flowchart.png differ
diff --git a/arrange-act-assert/etc/arrange-act-assert.urm.puml b/arrange-act-assert/etc/arrange-act-assert.urm.puml
new file mode 100644
index 000000000000..4b22d2a4e4bb
--- /dev/null
+++ b/arrange-act-assert/etc/arrange-act-assert.urm.puml
@@ -0,0 +1,11 @@
+@startuml
+package com.iluwatar.arrangeactassert {
+ class Cash {
+ - amount : int
+ ~ Cash(amount : int)
+ ~ count() : int
+ ~ minus(subtrahend : int) : boolean
+ ~ plus(addend : int)
+ }
+}
+@enduml
\ No newline at end of file
diff --git a/arrange-act-assert/pom.xml b/arrange-act-assert/pom.xml
new file mode 100644
index 000000000000..6b8ba1b2d490
--- /dev/null
+++ b/arrange-act-assert/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ arrange-act-assert
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
diff --git a/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java b/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java
new file mode 100644
index 000000000000..0c31b1f89f44
--- /dev/null
+++ b/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java
@@ -0,0 +1,57 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.arrangeactassert;
+
+import lombok.AllArgsConstructor;
+
+/**
+ * Arrange/Act/Assert (AAA) is a unit test pattern. In this simple example, we have a ({@link Cash})
+ * object for plus, minus and counting amount.
+ */
+@AllArgsConstructor
+public class Cash {
+
+ private int amount;
+
+ // plus
+ void plus(int addend) {
+ amount += addend;
+ }
+
+ // minus
+ boolean minus(int subtrahend) {
+ if (amount >= subtrahend) {
+ amount -= subtrahend;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // count
+ int count() {
+ return amount;
+ }
+}
diff --git a/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java
new file mode 100644
index 000000000000..ebb261277080
--- /dev/null
+++ b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java
@@ -0,0 +1,103 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.arrangeactassert;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Arrange/Act/Assert (AAA) is a pattern for organizing unit tests. It is a way to structure your
+ * tests, so they're easier to read, maintain and enhance.
+ *
+ * It breaks tests down into three clear and distinct steps:
+ *
+ *
1. Arrange: Perform the setup and initialization required for the test.
+ *
+ *
2. Act: Take action(s) required for the test.
+ *
+ *
3. Assert: Verify the outcome(s) of the test.
+ *
+ *
This pattern has several significant benefits. It creates a clear separation between a test's
+ * setup, operations, and results. This structure makes the code easier to read and understand. If
+ * you place the steps in order and format your code to separate them, you can scan a test and
+ * quickly comprehend what it does.
+ *
+ *
It also enforces a certain degree of discipline when you write your tests. You have to think
+ * clearly about the three steps your test will perform. But it makes tests more natural to write at
+ * the same time since you already have an outline.
+ *
+ *
In ({@link CashAAATest}) we have four test methods. Each of them has only one reason to change
+ * and one reason to fail. In a large and complicated code base, tests that honor the single
+ * responsibility principle are much easier to troubleshoot.
+ */
+class CashAAATest {
+
+ @Test
+ void testPlus() {
+ // Arrange
+ var cash = new Cash(3);
+ // Act
+ cash.plus(4);
+ // Assert
+ assertEquals(7, cash.count());
+ }
+
+ @Test
+ void testMinus() {
+ // Arrange
+ var cash = new Cash(8);
+ // Act
+ var result = cash.minus(5);
+ // Assert
+ assertTrue(result);
+ assertEquals(3, cash.count());
+ }
+
+ @Test
+ void testInsufficientMinus() {
+ // Arrange
+ var cash = new Cash(1);
+ // Act
+ var result = cash.minus(6);
+ // Assert
+ assertFalse(result);
+ assertEquals(1, cash.count());
+ }
+
+ @Test
+ void testUpdate() {
+ // Arrange
+ var cash = new Cash(5);
+ // Act
+ cash.plus(6);
+ var result = cash.minus(3);
+ // Assert
+ assertTrue(result);
+ assertEquals(8, cash.count());
+ }
+}
diff --git a/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java
new file mode 100644
index 000000000000..5756822516b8
--- /dev/null
+++ b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java
@@ -0,0 +1,60 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.arrangeactassert;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * ({@link CashAAATest}) is an anti-example of AAA pattern. This test is functionally correct, but
+ * with the addition of a new feature, it needs refactoring. There are an awful lot of steps in that
+ * test method, but it verifies the class' important behavior in just eleven lines. It violates the
+ * single responsibility principle. If this test method failed after a small code change, it might
+ * take some digging to discover why.
+ */
+class CashAntiAAATest {
+
+ @Test
+ void testCash() {
+ // initialize
+ var cash = new Cash(3);
+ // test plus
+ cash.plus(4);
+ assertEquals(7, cash.count());
+ // test minus
+ cash = new Cash(8);
+ assertTrue(cash.minus(5));
+ assertEquals(3, cash.count());
+ assertFalse(cash.minus(6));
+ assertEquals(3, cash.count());
+ // test update
+ cash.plus(5);
+ assertTrue(cash.minus(5));
+ assertEquals(3, cash.count());
+ }
+}
diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md
new file mode 100644
index 000000000000..519152e1268c
--- /dev/null
+++ b/async-method-invocation/README.md
@@ -0,0 +1,217 @@
+---
+title: "Async Method Invocation Pattern in Java: Elevate Performance with Asynchronous Programming"
+shortTitle: Async Method Invocation
+description: "Learn about the Async Method Invocation pattern in Java for asynchronous method calls, enhancing concurrency, scalability, and responsiveness in your applications. Explore real-world examples and code implementations."
+category: Concurrency
+language: en
+tag:
+ - Asynchronous
+ - Decoupling
+ - Reactive
+ - Scalability
+ - Thread management
+---
+
+## Also known as
+
+* Asynchronous Procedure Call
+
+## Intent of Async Method Invocation Design Pattern
+
+The Async Method Invocation pattern is designed to enhance concurrency by allowing methods to be called asynchronously. This pattern helps in executing parallel tasks, reducing wait times, and improving system throughput.
+
+## Detailed Explanation of Async Method Invocation Pattern with Real-World Examples
+
+Real-world example
+
+> Asynchronous method invocation enables non-blocking operations, allowing multiple processes to run concurrently. This pattern is particularly useful in applications requiring high scalability and performance, such as web servers and microservices.
+>
+> In the context of space rockets, an analogous example of the Async Method Invocation pattern can be seen in the communication between the mission control center and the onboard systems of the rocket. When mission control sends a command to the rocket to adjust its trajectory or perform a system check, they do not wait idly for the rocket to complete the task and report back. Instead, mission control continues to monitor other aspects of the mission and manage different tasks. The rocket executes the command asynchronously and sends a status update or result back to mission control once the operation is complete. This allows mission control to efficiently manage multiple concurrent operations without being blocked by any single task, similar to how asynchronous method invocation works in software systems.
+
+In plain words
+
+> Asynchronous method invocation starts task processing and returns immediately before the task is ready. The results of the task processing are returned to the caller later.
+
+Wikipedia says
+
+> In multithreaded computer programming, asynchronous method invocation (AMI), also known as asynchronous method calls or the asynchronous pattern is a design pattern in which the call site is not blocked while waiting for the called code to finish. Instead, the calling thread is notified when the reply arrives. Polling for a reply is an undesired option.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Async Method Invocation Pattern in Java
+
+Consider a scenario where multiple tasks need to be executed simultaneously. Using the Async Method Invocation pattern, you can initiate these tasks without waiting for each to complete, thus optimizing resource usage and reducing latency.
+
+In this example, we are launching space rockets and deploying lunar rovers.
+
+The application demonstrates the async method invocation pattern. The key parts of the pattern are`AsyncResult` which is an intermediate container for an asynchronously evaluated value, `AsyncCallback` which can be provided to be executed on task completion and `AsyncExecutor` that manages the execution of the async tasks.
+
+```java
+public interface AsyncResult {
+ boolean isCompleted();
+
+ T getValue() throws ExecutionException;
+
+ void await() throws InterruptedException;
+}
+```
+
+```java
+public interface AsyncCallback {
+ void onComplete(T value);
+
+ void onError(Exception ex);
+}
+```
+
+```java
+public interface AsyncExecutor {
+ AsyncResult startProcess(Callable task);
+
+ AsyncResult startProcess(Callable task, AsyncCallback callback);
+
+ T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException;
+}
+```
+
+`ThreadAsyncExecutor` is an implementation of `AsyncExecutor`. Some of its key parts are highlighted next.
+
+```java
+public class ThreadAsyncExecutor implements AsyncExecutor {
+
+ @Override
+ public AsyncResult startProcess(Callable task) {
+ return startProcess(task, null);
+ }
+
+ @Override
+ public AsyncResult startProcess(Callable task, AsyncCallback callback) {
+ var result = new CompletableResult<>(callback);
+ new Thread(
+ () -> {
+ try {
+ result.setValue(task.call());
+ } catch (Exception ex) {
+ result.setException(ex);
+ }
+ },
+ "executor-" + idx.incrementAndGet())
+ .start();
+ return result;
+ }
+
+ @Override
+ public T endProcess(AsyncResult asyncResult)
+ throws ExecutionException, InterruptedException {
+ if (!asyncResult.isCompleted()) {
+ asyncResult.await();
+ }
+ return asyncResult.getValue();
+ }
+}
+```
+
+Then we are ready to launch some rockets to see how everything works together.
+
+```java
+ public static void main(String[] args) throws Exception {
+ // construct a new executor that will run async tasks
+ var executor = new ThreadAsyncExecutor();
+
+ // start few async tasks with varying processing times, two last with callback handlers
+ final var asyncResult1 = executor.startProcess(lazyval(10, 500));
+ final var asyncResult2 = executor.startProcess(lazyval("test", 300));
+ final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
+ final var asyncResult4 = executor.startProcess(lazyval(20, 400),
+ callback("Deploying lunar rover"));
+ final var asyncResult5 =
+ executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
+
+ // emulate processing in the current thread while async tasks are running in their own threads
+ Thread.sleep(350); // Oh boy, we are working hard here
+ log("Mission command is sipping coffee");
+
+ // wait for completion of the tasks
+ final var result1 = executor.endProcess(asyncResult1);
+ final var result2 = executor.endProcess(asyncResult2);
+ final var result3 = executor.endProcess(asyncResult3);
+ asyncResult4.await();
+ asyncResult5.await();
+
+ // log the results of the tasks, callbacks log immediately when complete
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result1));
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result2));
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result3));
+}
+```
+
+Here's the program console output.
+
+```
+21:47:08.227[executor-2]INFO com.iluwatar.async.method.invocation.App-Space rocket launched successfully
+21:47:08.269[main]INFO com.iluwatar.async.method.invocation.App-Mission command is sipping coffee
+21:47:08.318[executor-4]INFO com.iluwatar.async.method.invocation.App-Space rocket<20>launched successfully
+21:47:08.335[executor-4]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover<20>
+21:47:08.414[executor-1]INFO com.iluwatar.async.method.invocation.App-Space rocket<10>launched successfully
+21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Space rocket launched successfully
+21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover
+21:47:08.616[executor-3]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launched successfully
+21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<10>launch complete
+21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket launch complete
+21:47:08.618[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launch complete
+```
+
+## When to Use the Async Method Invocation Pattern in Java
+
+This pattern is ideal for applications needing to manage multiple parallel tasks efficiently. It is commonly used in scenarios such as handling background processes, improving user interface responsiveness, and managing asynchronous data processing.
+
+Use the async method invocation pattern when
+
+* When operations do not need to complete before proceeding with the next steps in a program.
+* For tasks that are resource-intensive or time-consuming, such as IO operations, network requests, or complex computations, where making the operation synchronous would significantly impact performance or user experience.
+* In GUI applications to prevent freezing or unresponsiveness during long-running tasks.
+* In web applications for non-blocking IO operations.
+
+## Real-World Applications of Async Method Invocation Pattern in Java
+
+Many modern applications leverage the Async Method Invocation pattern, including web servers handling concurrent requests, microservices architectures, and systems requiring intensive background processing.
+
+* Web servers handling HTTP requests asynchronously to improve throughput and reduce latency.
+* Desktop and mobile applications using background threads to perform time-consuming operations without blocking the user interface.
+* Microservices architectures where services perform asynchronous communications via messaging queues or event streams.
+* [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html)
+* [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html)
+* [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html)
+* [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx)
+
+## Benefits and Trade-offs of Async Method Invocation Pattern
+
+While this pattern offers significant performance benefits, it also introduces complexity in error handling and resource management. Proper implementation is essential to avoid potential pitfalls such as race conditions and deadlocks.
+
+Benefits:
+
+* Improved Responsiveness: The main thread or application flow remains unblocked, improving the user experience in GUI applications and overall responsiveness.
+* Better Resource Utilization: By enabling parallel execution, system resources (like CPU and IO) are utilized more efficiently, potentially improving the application's throughput.
+* Scalability: Easier to scale applications, as tasks can be distributed across available resources more effectively.
+
+Trade-offs:
+
+* Complexity: Introducing asynchronous operations can complicate the codebase, making it more challenging to understand, debug, and maintain.
+* Resource Management: Requires careful management of threads or execution contexts, which can introduce overhead and potential resource exhaustion issues.
+* Error Handling: Asynchronous operations can make error handling more complex, as exceptions may occur in different threads or at different times.
+
+## Related Java Design Patterns
+
+The Async Method Invocation pattern often works well with other design patterns like the Command Pattern for encapsulating requests, the Observer Pattern for event handling, and the Promise Pattern for managing asynchronous results.
+
+* [Command](https://java-design-patterns.com/patterns/command/): Asynchronous method invocation can be used to implement the Command pattern, where commands are executed asynchronously.
+* [Observer](https://java-design-patterns.com/patterns/observer/): Asynchronous method invocation can be used to notify observers asynchronously when a subject's state changes.
+* [Promise](https://java-design-patterns.com/patterns/promise/): The AsyncResult interface can be considered a form of Promise, representing a value that may not be available yet.
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3Ti1N4f)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Java Concurrency in Practice](https://amzn.to/4ab97VU)
diff --git a/async-method-invocation/etc/async-method-invocation-sequence-diagram.png b/async-method-invocation/etc/async-method-invocation-sequence-diagram.png
new file mode 100644
index 000000000000..28420d783379
Binary files /dev/null and b/async-method-invocation/etc/async-method-invocation-sequence-diagram.png differ
diff --git a/async-method-invocation/etc/async-method-invocation.urm.png b/async-method-invocation/etc/async-method-invocation.urm.png
new file mode 100644
index 000000000000..898c1e199bda
Binary files /dev/null and b/async-method-invocation/etc/async-method-invocation.urm.png differ
diff --git a/async-method-invocation/etc/async-method-invocation.urm.puml b/async-method-invocation/etc/async-method-invocation.urm.puml
new file mode 100644
index 000000000000..d44665081856
--- /dev/null
+++ b/async-method-invocation/etc/async-method-invocation.urm.puml
@@ -0,0 +1,55 @@
+@startuml
+package com.iluwatar.async.method.invocation {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ - callback(name : String) : AsyncCallback {static}
+ - lazyval(value : T, delayMillis : long) : Callable {static}
+ - log(msg : String) {static}
+ + main(args : String[]) {static}
+ }
+ interface AsyncCallback {
+ + onComplete(T) {abstract}
+ + onError(Exception) {abstract}
+ }
+ interface AsyncExecutor {
+ + endProcess(AsyncResult) : T {abstract}
+ + startProcess(Callable) : AsyncResult {abstract}
+ + startProcess(Callable, AsyncCallback) : AsyncResult {abstract}
+ }
+ interface AsyncResult {
+ + await() {abstract}
+ + getValue() : T {abstract}
+ + isCompleted() : boolean {abstract}
+ }
+ class ThreadAsyncExecutor {
+ - idx : AtomicInteger
+ + ThreadAsyncExecutor()
+ + endProcess(asyncResult : AsyncResult) : T
+ + startProcess(task : Callable) : AsyncResult
+ + startProcess(task : Callable, callback : AsyncCallback) : AsyncResult
+ }
+ -class CompletableResult {
+ ~ COMPLETED : int {static}
+ ~ FAILED : int {static}
+ ~ RUNNING : int {static}
+ ~ callback : AsyncCallback
+ ~ exception : Exception
+ ~ lock : Object
+ ~ state : int
+ ~ value : T
+ ~ CompletableResult(callback : AsyncCallback)
+ + await()
+ + getValue() : T
+ ~ hasCallback() : boolean
+ + isCompleted() : boolean
+ ~ setException(exception : Exception)
+ ~ setValue(value : T)
+ }
+}
+ThreadAsyncExecutor ..|> AsyncCallback
+ThreadAsyncExecutor ..|> AsyncResult
+ThreadAsyncExecutor ..|> AsyncExecutor
+CompletableResult ..+ ThreadAsyncExecutor
+CompletableResult ..|> AsyncResult
+@enduml
diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml
new file mode 100644
index 000000000000..d9ddd918c8e9
--- /dev/null
+++ b/async-method-invocation/pom.xml
@@ -0,0 +1,75 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ async-method-invocation
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.async.method.invocation.App
+
+
+
+
+
+
+
+
+
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java
new file mode 100644
index 000000000000..ec3beed3be4d
--- /dev/null
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java
@@ -0,0 +1,134 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.async.method.invocation;
+
+import java.util.concurrent.Callable;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * In this example, we are launching space rockets and deploying lunar rovers.
+ *
+ * The application demonstrates the async method invocation pattern. The key parts of the pattern
+ * are AsyncResult
which is an intermediate container for an asynchronously evaluated
+ * value, AsyncCallback
which can be provided to be executed on task completion and
+ * AsyncExecutor
that manages the execution of the async tasks.
+ *
+ *
The main method shows example flow of async invocations. The main thread starts multiple tasks
+ * with variable durations and then continues its own work. When the main thread has done it's job
+ * it collects the results of the async tasks. Two of the tasks are handled with callbacks, meaning
+ * the callbacks are executed immediately when the tasks complete.
+ *
+ *
Noteworthy difference of thread usage between the async results and callbacks is that the
+ * async results are collected in the main thread but the callbacks are executed within the worker
+ * threads. This should be noted when working with thread pools.
+ *
+ *
Java provides its own implementations of async method invocation pattern. FutureTask,
+ * CompletableFuture and ExecutorService are the real world implementations of this pattern. But due
+ * to the nature of parallel programming, the implementations are not trivial. This example does not
+ * take all possible scenarios into account but rather provides a simple version that helps to
+ * understand the pattern.
+ *
+ * @see AsyncResult
+ * @see AsyncCallback
+ * @see AsyncExecutor
+ * @see java.util.concurrent.FutureTask
+ * @see java.util.concurrent.CompletableFuture
+ * @see java.util.concurrent.ExecutorService
+ */
+@Slf4j
+public class App {
+
+ private static final String ROCKET_LAUNCH_LOG_PATTERN = "Space rocket <%s> launched successfully";
+
+ /** Program entry point. */
+ public static void main(String[] args) throws Exception {
+ // construct a new executor that will run async tasks
+ var executor = new ThreadAsyncExecutor();
+
+ // start few async tasks with varying processing times, two last with callback handlers
+ final var asyncResult1 = executor.startProcess(lazyval(10, 500));
+ final var asyncResult2 = executor.startProcess(lazyval("test", 300));
+ final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
+ final var asyncResult4 =
+ executor.startProcess(lazyval(20, 400), callback("Deploying lunar rover"));
+ final var asyncResult5 =
+ executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
+
+ // emulate processing in the current thread while async tasks are running in their own threads
+ Thread.sleep(350); // Oh boy, we are working hard here
+ log("Mission command is sipping coffee");
+
+ // wait for completion of the tasks
+ final var result1 = executor.endProcess(asyncResult1);
+ final var result2 = executor.endProcess(asyncResult2);
+ final var result3 = executor.endProcess(asyncResult3);
+ asyncResult4.await();
+ asyncResult5.await();
+
+ // log the results of the tasks, callbacks log immediately when complete
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result1));
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result2));
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result3));
+ }
+
+ /**
+ * Creates a callable that lazily evaluates to given value with artificial delay.
+ *
+ * @param value value to evaluate
+ * @param delayMillis artificial delay in milliseconds
+ * @return new callable for lazy evaluation
+ */
+ private static Callable lazyval(T value, long delayMillis) {
+ return () -> {
+ Thread.sleep(delayMillis);
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, value));
+ return value;
+ };
+ }
+
+ /**
+ * Creates a simple callback that logs the complete status of the async result.
+ *
+ * @param name callback name
+ * @return new async callback
+ */
+ private static AsyncCallback callback(String name) {
+ return new AsyncCallback<>() {
+ @Override
+ public void onComplete(T value) {
+ log(name + " <" + value + ">");
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ log(name + " failed: " + ex.getMessage());
+ }
+ };
+ }
+
+ private static void log(String msg) {
+ LOGGER.info(msg);
+ }
+}
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java
new file mode 100644
index 000000000000..2dfbaf9a188a
--- /dev/null
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java
@@ -0,0 +1,47 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.async.method.invocation;
+
+/**
+ * AsyncCallback interface.
+ *
+ * @param Type of Result
+ */
+public interface AsyncCallback {
+
+ /**
+ * Complete handler which is executed when async task is completed.
+ *
+ * @param value the evaluated value from async task
+ */
+ void onComplete(T value);
+
+ /**
+ * Error handler which is executed when async task fails execution.
+ *
+ * @param ex exception which was thrown during async task execution(non-null)
+ */
+ void onError(Exception ex);
+}
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
new file mode 100644
index 000000000000..3bae90830098
--- /dev/null
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
@@ -0,0 +1,61 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.async.method.invocation;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+/** AsyncExecutor interface. */
+public interface AsyncExecutor {
+
+ /**
+ * Starts processing of an async task. Returns immediately with async result.
+ *
+ * @param task task to be executed asynchronously
+ * @return async result for the task
+ */
+ AsyncResult startProcess(Callable task);
+
+ /**
+ * Starts processing of an async task. Returns immediately with async result. Executes callback
+ * when the task is completed.
+ *
+ * @param task task to be executed asynchronously
+ * @param callback callback to be executed on task completion
+ * @return async result for the task
+ */
+ AsyncResult startProcess(Callable task, AsyncCallback callback);
+
+ /**
+ * Ends processing of an async task. Blocks the current thread if necessary and returns the
+ * evaluated value of the completed task.
+ *
+ * @param asyncResult async result of a task
+ * @return evaluated value of the completed task
+ * @throws ExecutionException if execution has failed, containing the root cause
+ * @throws InterruptedException if the execution is interrupted
+ */
+ T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException;
+}
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
new file mode 100644
index 000000000000..3eebdc4e773d
--- /dev/null
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
@@ -0,0 +1,58 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.async.method.invocation;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * AsyncResult interface.
+ *
+ * @param parameter returned when getValue is invoked
+ */
+public interface AsyncResult {
+
+ /**
+ * Status of the async task execution.
+ *
+ * @return true
if execution is completed or failed
+ */
+ boolean isCompleted();
+
+ /**
+ * Gets the value of completed async task.
+ *
+ * @return evaluated value or throws ExecutionException if execution has failed
+ * @throws ExecutionException if execution has failed, containing the root cause
+ * @throws IllegalStateException if execution is not completed
+ */
+ T getValue() throws ExecutionException;
+
+ /**
+ * Blocks the current thread until the async task is completed.
+ *
+ * @throws InterruptedException if the execution is interrupted
+ */
+ void await() throws InterruptedException;
+}
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java
new file mode 100644
index 000000000000..a1261f34184c
--- /dev/null
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java
@@ -0,0 +1,156 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.async.method.invocation;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** Implementation of async executor that creates a new thread for every task. */
+public class ThreadAsyncExecutor implements AsyncExecutor {
+
+ /** Index for thread naming. */
+ private final AtomicInteger idx = new AtomicInteger(0);
+
+ @Override
+ public AsyncResult startProcess(Callable task) {
+ return startProcess(task, null);
+ }
+
+ @Override
+ public AsyncResult startProcess(Callable task, AsyncCallback callback) {
+ var result = new CompletableResult<>(callback);
+ new Thread(
+ () -> {
+ try {
+ result.setValue(task.call());
+ } catch (Exception ex) {
+ result.setException(ex);
+ }
+ },
+ "executor-" + idx.incrementAndGet())
+ .start();
+ return result;
+ }
+
+ @Override
+ public T endProcess(AsyncResult asyncResult)
+ throws ExecutionException, InterruptedException {
+ if (!asyncResult.isCompleted()) {
+ asyncResult.await();
+ }
+ return asyncResult.getValue();
+ }
+
+ /**
+ * Simple implementation of async result that allows completing it successfully with a value or
+ * exceptionally with an exception. A really simplified version from its real life cousins
+ * FutureTask and CompletableFuture.
+ *
+ * @see java.util.concurrent.FutureTask
+ * @see java.util.concurrent.CompletableFuture
+ */
+ private static class CompletableResult implements AsyncResult {
+
+ static final int RUNNING = 1;
+ static final int FAILED = 2;
+ static final int COMPLETED = 3;
+
+ final Object lock;
+ final AsyncCallback callback;
+
+ volatile int state = RUNNING;
+ T value;
+ Exception exception;
+
+ CompletableResult(AsyncCallback callback) {
+ this.lock = new Object();
+ this.callback = callback;
+ }
+
+ boolean hasCallback() {
+ return callback != null;
+ }
+
+ /**
+ * Sets the value from successful execution and executes callback if available. Notifies any
+ * thread waiting for completion.
+ *
+ * @param value value of the evaluated task
+ */
+ void setValue(T value) {
+ this.value = value;
+ this.state = COMPLETED;
+ if (hasCallback()) {
+ callback.onComplete(value);
+ }
+ synchronized (lock) {
+ lock.notifyAll();
+ }
+ }
+
+ /**
+ * Sets the exception from failed execution and executes callback if available. Notifies any
+ * thread waiting for completion.
+ *
+ * @param exception exception of the failed task
+ */
+ void setException(Exception exception) {
+ this.exception = exception;
+ this.state = FAILED;
+ if (hasCallback()) {
+ callback.onError(exception);
+ }
+ synchronized (lock) {
+ lock.notifyAll();
+ }
+ }
+
+ @Override
+ public boolean isCompleted() {
+ return state > RUNNING;
+ }
+
+ @Override
+ public T getValue() throws ExecutionException {
+ if (state == COMPLETED) {
+ return value;
+ } else if (state == FAILED) {
+ throw new ExecutionException(exception);
+ } else {
+ throw new IllegalStateException("Execution not completed yet");
+ }
+ }
+
+ @Override
+ public void await() throws InterruptedException {
+ synchronized (lock) {
+ while (!isCompleted()) {
+ lock.wait();
+ }
+ }
+ }
+ }
+}
diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java
new file mode 100644
index 000000000000..d58a3f6b5c30
--- /dev/null
+++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java
@@ -0,0 +1,45 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.async.method.invocation;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Application 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} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java
new file mode 100644
index 000000000000..d6540ce77309
--- /dev/null
+++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java
@@ -0,0 +1,349 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.async.method.invocation;
+
+import static java.time.Duration.ofMillis;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** ThreadAsyncExecutorTest */
+class ThreadAsyncExecutorTest {
+
+ @Captor private ArgumentCaptor exceptionCaptor;
+
+ @Mock private Callable task;
+
+ @Mock private AsyncCallback callback;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ /** Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} */
+ @Test
+ void testSuccessfulTaskWithoutCallback() {
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call()).thenReturn(result);
+
+ final var asyncResult = executor.startProcess(task);
+ assertNotNull(asyncResult);
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ // Our task should only execute once ...
+ verify(task, times(1)).call();
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
+ }
+
+ /**
+ * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable,
+ * AsyncCallback)}
+ */
+ @Test
+ void testSuccessfulTaskWithCallback() {
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call()).thenReturn(result);
+
+ final var asyncResult = executor.startProcess(task, callback);
+ assertNotNull(asyncResult);
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ // Our task should only execute once ...
+ verify(task, times(1)).call();
+
+ // ... same for the callback, we expect our object
+ verify(callback, times(1)).onComplete(eq(result));
+ verify(callback, times(0)).onError(exceptionCaptor.capture());
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
+ }
+
+ /**
+ * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a
+ * task takes a while to execute
+ */
+ @Test
+ void testLongRunningTaskWithoutCallback() {
+ assertTimeout(
+ ofMillis(5000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call())
+ .thenAnswer(
+ i -> {
+ Thread.sleep(1500);
+ return result;
+ });
+
+ final var asyncResult = executor.startProcess(task);
+ assertNotNull(asyncResult);
+ assertFalse(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail(
+ "Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ // Our task should only execute once, but it can take a while ...
+ verify(task, timeout(3000).times(1)).call();
+
+ // Prevent timing issues, and wait until the result is available
+ asyncResult.await();
+ assertTrue(asyncResult.isCompleted());
+ verifyNoMoreInteractions(task);
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
+ }
+
+ /**
+ * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable,
+ * AsyncCallback)} when a task takes a while to execute
+ */
+ @Test
+ void testLongRunningTaskWithCallback() {
+ assertTimeout(
+ ofMillis(5000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call())
+ .thenAnswer(
+ i -> {
+ Thread.sleep(1500);
+ return result;
+ });
+
+ final var asyncResult = executor.startProcess(task, callback);
+ assertNotNull(asyncResult);
+ assertFalse(asyncResult.isCompleted());
+
+ verifyNoMoreInteractions(callback);
+
+ try {
+ asyncResult.getValue();
+ fail(
+ "Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ // Our task should only execute once, but it can take a while ...
+ verify(task, timeout(3000).times(1)).call();
+ verify(callback, timeout(3000).times(1)).onComplete(eq(result));
+ verify(callback, times(0)).onError(isA(Exception.class));
+
+ // Prevent timing issues, and wait until the result is available
+ asyncResult.await();
+ assertTrue(asyncResult.isCompleted());
+ verifyNoMoreInteractions(task, callback);
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
+ }
+
+ /**
+ * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a
+ * task takes a while to execute, while waiting on the result using {@link
+ * ThreadAsyncExecutor#endProcess(AsyncResult)}
+ */
+ @Test
+ void testEndProcess() {
+ assertTimeout(
+ ofMillis(5000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call())
+ .thenAnswer(
+ i -> {
+ Thread.sleep(1500);
+ return result;
+ });
+
+ final var asyncResult = executor.startProcess(task);
+ assertNotNull(asyncResult);
+ assertFalse(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail(
+ "Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ assertSame(result, executor.endProcess(asyncResult));
+ verify(task, times(1)).call();
+ assertTrue(asyncResult.isCompleted());
+
+ // Calling end process a second time while already finished should give the same result
+ assertSame(result, executor.endProcess(asyncResult));
+ verifyNoMoreInteractions(task);
+ });
+ }
+
+ /**
+ * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when
+ * the callable is 'null'
+ */
+ @Test
+ void testNullTask() {
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+ final var asyncResult = executor.startProcess(null);
+
+ assertNotNull(
+ asyncResult,
+ "The AsyncResult should not be 'null', even though the task was 'null'.");
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail("Expected ExecutionException with NPE as cause");
+ } catch (final ExecutionException e) {
+ assertNotNull(e.getMessage());
+ assertNotNull(e.getCause());
+ assertEquals(NullPointerException.class, e.getCause().getClass());
+ }
+ });
+ }
+
+ /**
+ * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable,
+ * AsyncCallback)} when the callable is 'null', but the asynchronous callback is provided
+ */
+ @Test
+ void testNullTaskWithCallback() {
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+ final var asyncResult = executor.startProcess(null, callback);
+
+ assertNotNull(
+ asyncResult,
+ "The AsyncResult should not be 'null', even though the task was 'null'.");
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+ verify(callback, times(0)).onComplete(any());
+ verify(callback, times(1)).onError(exceptionCaptor.capture());
+
+ final var exception = exceptionCaptor.getValue();
+ assertNotNull(exception);
+
+ assertEquals(NullPointerException.class, exception.getClass());
+
+ try {
+ asyncResult.getValue();
+ fail("Expected ExecutionException with NPE as cause");
+ } catch (final ExecutionException e) {
+ assertNotNull(e.getMessage());
+ assertNotNull(e.getCause());
+ assertEquals(NullPointerException.class, e.getCause().getClass());
+ }
+ });
+ }
+
+ /**
+ * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable,
+ * AsyncCallback)} when both the callable and the asynchronous callback are 'null'
+ */
+ @Test
+ void testNullTaskWithNullCallback() {
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+ final var asyncResult = executor.startProcess(null, null);
+
+ assertNotNull(
+ asyncResult,
+ "The AsyncResult should not be 'null', even though the task and callback were 'null'.");
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail("Expected ExecutionException with NPE as cause");
+ } catch (final ExecutionException e) {
+ assertNotNull(e.getMessage());
+ assertNotNull(e.getCause());
+ assertEquals(NullPointerException.class, e.getCause().getClass());
+ }
+ });
+ }
+}
diff --git a/backpressure/README.md b/backpressure/README.md
new file mode 100644
index 000000000000..d0734ad1f934
--- /dev/null
+++ b/backpressure/README.md
@@ -0,0 +1,156 @@
+---
+title: "Backpressure Pattern in Java: Gracefully regulate producer-to-consumer data flow to prevent overload."
+shortTitle: Backpressure
+description: "Dive into the Backpressure design pattern in Java through practical examples, discovering how it prevents overload while ensuring stability and peak performance by aligning data flow with consumer capacity."
+category: Concurrency
+language: en
+tag:
+ - Asynchronous
+ - Event-driven
+ - Reactive
+ - Resilience
+---
+
+## Also known as
+
+* Flow Control
+* Rate Limiting Mechanism
+
+## Intent of the Backpressure Design Pattern
+
+Control the rate of data production so downstream consumers are not overwhelmed by excessive load.
+
+## Detailed Explanation of Backpressure Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a busy coffee shop where multiple baristas brew drinks (producers), and a single barista is responsible for carefully crafting latte art (consumer). If drinks are brewed faster than the latte-art barista can decorate them, they pile up, risking quality issues or discarded drinks. By introducing a pacing system—only sending new cups once the latte-art barista is ready—everyone stays synchronized, ensuring minimal waste and a consistently enjoyable customer experience.
+
+In plain words
+
+> The Backpressure design pattern is a flow control mechanism that prevents overwhelming a system by regulating data production based on the consumer’s processing capacity.
+
+Wikipedia says
+
+> Back pressure (or backpressure) is the term for a resistance to the desired flow of fluid through pipes. Obstructions or tight bends create backpressure via friction loss and pressure drop. In distributed systems in particular event-driven architecture, back pressure is a technique to regulate flow of data, ensuring that components do not become overwhelmed.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Backpressure Pattern in Java
+
+This example demonstrates how backpressure can be implemented using Project Reactor. We begin by creating a simple publisher that emits a stream of integers, introducing a small delay to mimic a slower production rate:
+
+```java
+public class Publisher {
+ public static Flux publish(int start, int count, int delay) {
+ return Flux.range(start, count).delayElements(Duration.ofMillis(delay)).log();
+ }
+}
+```
+
+Next, we define a custom subscriber by extending Reactor’s BaseSubscriber. It simulates slow processing by sleeping for 500ms per item. Initially, the subscriber requests ten items; for every five items processed, it requests five more:
+
+```java
+public class Subscriber extends BaseSubscriber {
+
+ private static final Logger logger = LoggerFactory.getLogger(Subscriber.class);
+
+ @Override
+ protected void hookOnSubscribe(@NonNull Subscription subscription) {
+ logger.info("subscribe()");
+ request(10); //request 10 items initially
+ }
+
+ @Override
+ protected void hookOnNext(@NonNull Integer value) {
+ processItem();
+ logger.info("process({})", value);
+ if (value % 5 == 0) {
+ // request for the next 5 items after processing first 5
+ request(5);
+ }
+ }
+
+ @Override
+ protected void hookOnComplete() {
+ //completed processing.
+ }
+
+ private void processItem() {
+ try {
+ Thread.sleep(500); // simulate slow processing
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+}
+```
+
+Finally, in the `main` method, we publish a range of integers and subscribe using the custom subscriber. A short sleep in the main thread allows the emission, backpressure requests, and processing to be fully observed:
+
+```java
+public static void main(String[] args) throws InterruptedException {
+ Subscriber sub = new Subscriber();
+ Publisher.publish(1, 8, 200).subscribe(sub);
+ Thread.sleep(5000); //wait for execution
+
+}
+```
+
+Below is an example of the program’s output. It shows the subscriber’s log entries, including when it requests more data and when each integer is processed:
+
+```
+23:09:55.746 [main] DEBUG reactor.util.Loggers -- Using Slf4j logging framework
+23:09:55.762 [main] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onSubscribe(FluxConcatMapNoPrefetch.FluxConcatMapNoPrefetchSubscriber)
+23:09:55.762 [main] INFO com.iluwatar.backpressure.Subscriber -- subscribe()
+23:09:55.763 [main] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- request(10)
+23:09:55.969 [parallel-1] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(1)
+23:09:56.475 [parallel-1] INFO com.iluwatar.backpressure.Subscriber -- process(1)
+23:09:56.680 [parallel-2] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(2)
+23:09:57.185 [parallel-2] INFO com.iluwatar.backpressure.Subscriber -- process(2)
+23:09:57.389 [parallel-3] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(3)
+23:09:57.894 [parallel-3] INFO com.iluwatar.backpressure.Subscriber -- process(3)
+23:09:58.099 [parallel-4] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(4)
+23:09:58.599 [parallel-4] INFO com.iluwatar.backpressure.Subscriber -- process(4)
+23:09:58.805 [parallel-5] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(5)
+23:09:59.311 [parallel-5] INFO com.iluwatar.backpressure.Subscriber -- process(5)
+23:09:59.311 [parallel-5] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- request(5)
+23:09:59.516 [parallel-6] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(6)
+23:10:00.018 [parallel-6] INFO com.iluwatar.backpressure.Subscriber -- process(6)
+23:10:00.223 [parallel-7] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(7)
+23:10:00.729 [parallel-7] INFO com.iluwatar.backpressure.Subscriber -- process(7)
+23:10:00.930 [parallel-8] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(8)
+23:10:01.436 [parallel-8] INFO com.iluwatar.backpressure.Subscriber -- process(8)
+23:10:01.437 [parallel-8] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onComplete()
+```
+
+## When to Use the Backpressure Pattern In Java
+
+* Use in Java systems where data is produced at high velocity and consumers risk overload.
+* Applicable in reactive or event-driven architectures to maintain stability under varying load conditions.
+
+## Benefits and Trade-offs of Backpressure Pattern
+
+Benefits:
+
+* Protects consumers from saturation and resource exhaustion.
+
+Trade-offs:
+
+* Introduces possible delays if production must slow down to match consumer capacity.
+* Requires careful orchestration in complex systems with multiple concurrent data sources.
+
+## Related Java Design Patterns
+
+* [Observer Pattern](https://java-design-patterns.com/patterns/observer/): Both patterns involve a producer notifying consumers. Observer is synchronous and tightly coupled (observers know the subject).
+* [Publish-Subscribe Pattern](https://java-design-patterns.com/patterns/publish-subscribe/): Both patterns deal with asynchronous data flow and can work together to manage message distribution and consumption effectively.
+
+## References and Credits
+
+* [Backpressure Explained (RedHat Developers Blog)](https://developers.redhat.com/articles/backpressure-explained)
+* [Hands-On Reactive Programming in Spring 5](https://amzn.to/3YuYfyO)
+* [Reactive Programming with RxJava: Creating Asynchronous, Event-Based Applications](https://amzn.to/42negbf)
+* [Reactive Streams in Java](https://amzn.to/3RJjUzA)
+* [Reactive Streams Specification](https://www.reactive-streams.org/)
diff --git a/backpressure/etc/backpressure-sequence-diagram.png b/backpressure/etc/backpressure-sequence-diagram.png
new file mode 100644
index 000000000000..b74a5d2a5362
Binary files /dev/null and b/backpressure/etc/backpressure-sequence-diagram.png differ
diff --git a/backpressure/etc/backpressure.png b/backpressure/etc/backpressure.png
new file mode 100644
index 000000000000..0de9ff54b673
Binary files /dev/null and b/backpressure/etc/backpressure.png differ
diff --git a/backpressure/pom.xml b/backpressure/pom.xml
new file mode 100644
index 000000000000..fcc15892fb8a
--- /dev/null
+++ b/backpressure/pom.xml
@@ -0,0 +1,81 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+
+ backpressure
+
+
+
+ io.projectreactor
+ reactor-core
+ 3.8.0-M1
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ io.projectreactor
+ reactor-test
+ 3.8.0-M1
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.backpressure.App
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backpressure/src/main/java/com/iluwatar/backpressure/App.java b/backpressure/src/main/java/com/iluwatar/backpressure/App.java
new file mode 100644
index 000000000000..4632c42a467f
--- /dev/null
+++ b/backpressure/src/main/java/com/iluwatar/backpressure/App.java
@@ -0,0 +1,76 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.backpressure;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * The Backpressure pattern is a flow control mechanism. It allows a consumer to signal to a
+ * producer to slow down or stop sending data when it's overwhelmed.
+ * Prevents memory overflow, CPU thrashing, and resource exhaustion.
+ * Ensures fair usage of resources in distributed systems.
+ * Avoids buffer bloat and latency spikes. Key concepts of this design paradigm involves
+ * Publisher/Producer: Generates data.
+ * Subscriber/Consumer: Receives and processes data.
+ *
+ * In this example we will create a {@link Publisher} and a {@link Subscriber}. Publisher
+ * will emit a stream of integer values with a predefined delay. Subscriber takes 500 ms to
+ * process one integer. Since the subscriber can't process the items fast enough we apply
+ * backpressure to the publisher so that it will request 10 items first, process 5 items and
+ * request for the next 5 again. After processing 5 items subscriber will keep requesting for
+ * another 5 until the stream ends.
+ */
+public class App {
+
+ protected static CountDownLatch latch;
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) throws InterruptedException {
+
+ /*
+ * This custom subscriber applies backpressure:
+ * - Has a processing delay of 0.5 milliseconds
+ * - Requests 10 items initially
+ * - Process 5 items and request for the next 5 items
+ */
+ Subscriber sub = new Subscriber();
+ // slow publisher emit 15 numbers with a delay of 200 milliseconds
+ Publisher.publish(1, 17, 200).subscribe(sub);
+
+ latch = new CountDownLatch(1);
+ latch.await();
+
+ sub = new Subscriber();
+ // fast publisher emit 15 numbers with a delay of 1 millisecond
+ Publisher.publish(1, 17, 1).subscribe(sub);
+
+ latch = new CountDownLatch(1);
+ latch.await();
+ }
+}
diff --git a/backpressure/src/main/java/com/iluwatar/backpressure/Publisher.java b/backpressure/src/main/java/com/iluwatar/backpressure/Publisher.java
new file mode 100644
index 000000000000..1d39a070ae57
--- /dev/null
+++ b/backpressure/src/main/java/com/iluwatar/backpressure/Publisher.java
@@ -0,0 +1,46 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.backpressure;
+
+import java.time.Duration;
+import reactor.core.publisher.Flux;
+
+/** This class is the publisher that generates the data stream. */
+public class Publisher {
+
+ private Publisher() {}
+
+ /**
+ * On message method will trigger when the subscribed event is published.
+ *
+ * @param start starting integer
+ * @param count how many integers to emit
+ * @param delay delay between each item in milliseconds
+ * @return a flux stream of integers
+ */
+ public static Flux publish(int start, int count, int delay) {
+ return Flux.range(start, count).delayElements(Duration.ofMillis(delay)).log();
+ }
+}
diff --git a/backpressure/src/main/java/com/iluwatar/backpressure/Subscriber.java b/backpressure/src/main/java/com/iluwatar/backpressure/Subscriber.java
new file mode 100644
index 000000000000..40ff5aebc814
--- /dev/null
+++ b/backpressure/src/main/java/com/iluwatar/backpressure/Subscriber.java
@@ -0,0 +1,64 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.backpressure;
+
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.reactivestreams.Subscription;
+import reactor.core.publisher.BaseSubscriber;
+
+/** This class is the custom subscriber that subscribes to the data stream. */
+@Slf4j
+public class Subscriber extends BaseSubscriber {
+
+ @Override
+ protected void hookOnSubscribe(@NonNull Subscription subscription) {
+ request(10); // request 10 items initially
+ }
+
+ @Override
+ protected void hookOnNext(@NonNull Integer value) {
+ processItem();
+ LOGGER.info("process({})", value);
+ if (value % 5 == 0) {
+ // request for the next 5 items after processing first 5
+ request(5);
+ }
+ }
+
+ @Override
+ protected void hookOnComplete() {
+ App.latch.countDown();
+ }
+
+ private void processItem() {
+ try {
+ Thread.sleep(500); // simulate slow processing
+ } catch (InterruptedException e) {
+ LOGGER.error(e.getMessage(), e);
+ Thread.currentThread().interrupt();
+ }
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/AppTest.java b/backpressure/src/test/java/com/iluwatar/backpressure/AppTest.java
new file mode 100644
index 000000000000..8fe2245a97b7
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/AppTest.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.backpressure;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+public class AppTest {
+
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/LoggerExtension.java b/backpressure/src/test/java/com/iluwatar/backpressure/LoggerExtension.java
new file mode 100644
index 000000000000..e99926e00a1a
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/LoggerExtension.java
@@ -0,0 +1,60 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.backpressure;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.slf4j.LoggerFactory;
+
+public class LoggerExtension implements BeforeEachCallback, AfterEachCallback {
+
+ private final ListAppender listAppender = new ListAppender<>();
+ private final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+
+ @Override
+ public void afterEach(ExtensionContext extensionContext) {
+ listAppender.stop();
+ listAppender.list.clear();
+ logger.detachAppender(listAppender);
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext extensionContext) {
+ logger.addAppender(listAppender);
+ listAppender.start();
+ }
+
+ public List getFormattedMessages() {
+ return listAppender.list.stream()
+ .map(ILoggingEvent::getFormattedMessage)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/PublisherTest.java b/backpressure/src/test/java/com/iluwatar/backpressure/PublisherTest.java
new file mode 100644
index 000000000000..010a80e83959
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/PublisherTest.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.backpressure;
+
+import static com.iluwatar.backpressure.Publisher.publish;
+
+import java.time.Duration;
+import org.junit.jupiter.api.Test;
+import reactor.core.publisher.Flux;
+import reactor.test.StepVerifier;
+
+public class PublisherTest {
+
+ @Test
+ public void testPublish() {
+
+ Flux flux = publish(1, 3, 200);
+
+ StepVerifier.withVirtualTime(() -> flux)
+ .expectSubscription()
+ .expectNoEvent(Duration.ofMillis(200))
+ .expectNext(1)
+ .expectNoEvent(Duration.ofSeconds(200))
+ .expectNext(2)
+ .expectNoEvent(Duration.ofSeconds(200))
+ .expectNext(3)
+ .verifyComplete();
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/SubscriberTest.java b/backpressure/src/test/java/com/iluwatar/backpressure/SubscriberTest.java
new file mode 100644
index 000000000000..7f7676612d02
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/SubscriberTest.java
@@ -0,0 +1,54 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.backpressure;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.concurrent.CountDownLatch;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class SubscriberTest {
+
+ @RegisterExtension public LoggerExtension loggerExtension = new LoggerExtension();
+
+ @Test
+ public void testSubscribe() throws InterruptedException {
+
+ Subscriber sub = new Subscriber();
+ Publisher.publish(1, 8, 100).subscribe(sub);
+
+ App.latch = new CountDownLatch(1);
+ App.latch.await();
+
+ String result = String.join(",", loggerExtension.getFormattedMessages());
+ assertTrue(
+ result.endsWith(
+ "onSubscribe(FluxConcatMapNoPrefetch."
+ + "FluxConcatMapNoPrefetchSubscriber),request(10),onNext(1),process(1),onNext(2),"
+ + "process(2),onNext(3),process(3),onNext(4),process(4),onNext(5),process(5),request(5),"
+ + "onNext(6),process(6),onNext(7),process(7),onNext(8),process(8),onComplete()"));
+ }
+}
diff --git a/balking/README.md b/balking/README.md
new file mode 100644
index 000000000000..cdd09cf5b7d1
--- /dev/null
+++ b/balking/README.md
@@ -0,0 +1,161 @@
+---
+title: "Balking Pattern in Java: Smart Control Over Java Execution"
+shortTitle: Balking
+description: "Learn the Balking design pattern in Java, a concurrency pattern that prevents code execution in inappropriate states. Discover examples, use cases, and benefits."
+category: Concurrency
+language: en
+tag:
+ - Concurrency
+ - Decoupling
+ - Fault tolerance
+ - Synchronization
+---
+
+## Intent of Balking Design Pattern
+
+The Balking Pattern in Java is a concurrency design pattern that prevents an object from executing certain code if it is in an incomplete or inappropriate state. This pattern is crucial for managing state and concurrency in multithreaded Java applications.
+
+## Detailed Explanation of Balking Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world analogy of the Balking design pattern can be seen in a laundry service. Imagine a washing machine at a laundromat that only starts washing clothes if the door is properly closed and locked. If a user tries to start the machine while the door is open, the machine balks and does nothing. This ensures that the washing process only begins when it is safe to do so, preventing water spillage and potential damage to the machine. Similarly, the Balking pattern in software design ensures that operations are only executed when the object is in an appropriate state, preventing erroneous actions and maintaining system stability.
+
+In plain words
+
+> Using the balking pattern, a certain code executes only if the object is in particular state.
+
+Wikipedia says
+
+> The balking pattern is a software design pattern that only executes an action on an object when the object is in a particular state. For example, if an object reads ZIP files and a calling method invokes a get method on the object when the ZIP file is not open, the object would "balk" at the request.
+
+Flowchart
+
+
+
+## Programmatic Example of Balking Pattern in Java
+
+This example demonstrates the Balking Pattern in a multithreaded Java application, highlighting state management and concurrency control. The Balking Pattern is exemplified by a washing machine's start button that initiates washing only if the machine is idle. This ensures state management and prevents concurrent issues.
+
+There's a start-button in a washing machine to initiate the laundry washing. When the washing machine is inactive the button works as expected, but if it's already washing the button does nothing.
+
+In this example implementation, `WashingMachine` is an object that has two states in which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING using a thread-safe method. On the other hand, if it already has been washing and any other thread executes `wash`it won't do that and returns without doing anything.
+
+Here are the relevant parts of the `WashingMachine` class.
+
+```java
+
+@Slf4j
+public class WashingMachine {
+
+ private final DelayProvider delayProvider;
+ private WashingMachineState washingMachineState;
+
+ public WashingMachine(DelayProvider delayProvider) {
+ this.delayProvider = delayProvider;
+ this.washingMachineState = WashingMachineState.ENABLED;
+ }
+
+ public WashingMachineState getWashingMachineState() {
+ return washingMachineState;
+ }
+
+ public void wash() {
+ synchronized (this) {
+ var machineState = getWashingMachineState();
+ LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState);
+ if (this.washingMachineState == WashingMachineState.WASHING) {
+ LOGGER.error("Cannot wash if the machine has been already washing!");
+ return;
+ }
+ this.washingMachineState = WashingMachineState.WASHING;
+ }
+ LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
+ this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
+ }
+
+ public synchronized void endOfWashing() {
+ washingMachineState = WashingMachineState.ENABLED;
+ LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
+ }
+}
+```
+
+Here's the simple `DelayProvider` interface used by the `WashingMachine`.
+
+```java
+public interface DelayProvider {
+ void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
+}
+```
+
+Now, we introduce the application using the `WashingMachine`.
+
+```java
+public static void main(String... args) {
+ final var washingMachine = new WashingMachine();
+ var executorService = Executors.newFixedThreadPool(3);
+ for (int i = 0; i < 3; i++) {
+ executorService.execute(washingMachine::wash);
+ }
+ executorService.shutdown();
+ try {
+ if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
+ executorService.shutdownNow();
+ }
+ } catch (InterruptedException ie) {
+ LOGGER.error("ERROR: Waiting on executor service shutdown!");
+ Thread.currentThread().interrupt();
+ }
+}
+```
+
+Here is the console output of the program.
+
+```
+14:02:52.268 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Actual machine state: ENABLED
+14:02:52.272 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Doing the washing
+14:02:52.272 [pool-1-thread-3] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-3: Actual machine state: WASHING
+14:02:52.273 [pool-1-thread-3] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing!
+14:02:52.273 [pool-1-thread-1] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-1: Actual machine state: WASHING
+14:02:52.273 [pool-1-thread-1] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing!
+14:02:52.324 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed.
+```
+
+## When to Use the Balking Pattern in Java
+
+Use the Balking pattern when
+
+* You want to invoke an action on an object only when it is in a particular state
+* Objects are generally only in a state that is prone to balking temporarily but for an unknown amount of time
+* In multithreaded applications where certain actions should only proceed when specific conditions are met, and those conditions are expected to change over time due to external factors or concurrent operations.
+
+## Real-World Applications of Balking Pattern in Java
+
+* Resource pooling, where resources are only allocated if they are in a valid state for allocation.
+* Thread management, where threads only proceed with tasks if certain conditions (like task availability or resource locks) are met.
+
+## Benefits and Trade-offs of Balking Pattern
+
+Benefits:
+
+* Reduces unnecessary lock acquisitions in situations where actions cannot proceed, enhancing performance in concurrent applications.
+* Encourages clear separation of state management and behavior, leading to cleaner code.
+* Simplifies the handling of operations that should only be performed under certain conditions without cluttering the caller code with state checks.
+
+Trade-offs:
+
+* Can introduce complexity by obscuring the conditions under which actions are taken or ignored, potentially making the system harder to debug and understand.
+* May lead to missed opportunities or actions if the state changes are not properly monitored or if the balking condition is too restrictive.
+
+## Related Java Design Patterns
+
+* [Double-Checked Locking](https://java-design-patterns.com/patterns/double-checked-locking/): Ensures that initialization occurs only when necessary and avoids unnecessary locking, which is related to Balking in terms of conditionally executing logic based on the object's state.
+* [Guarded Suspension](https://java-design-patterns.com/patterns/guarded-suspension/): Similar in ensuring actions are only performed when an object is in a certain state, but typically involves waiting until the state is valid.
+* [State](https://java-design-patterns.com/patterns/state/): The State pattern can be used in conjunction with Balking to manage the states and transitions of the object.
+
+## References and Credits
+
+* [Concurrent Programming in Java : Design Principles and Patterns](https://amzn.to/4dIBqxL)
+* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
+* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML](https://amzn.to/4bOtzwF)
diff --git a/balking/etc/balking-flowchart.png b/balking/etc/balking-flowchart.png
new file mode 100644
index 000000000000..400a1ce8d3e3
Binary files /dev/null and b/balking/etc/balking-flowchart.png differ
diff --git a/balking/etc/balking.png b/balking/etc/balking.png
new file mode 100644
index 000000000000..f409eaacbb95
Binary files /dev/null and b/balking/etc/balking.png differ
diff --git a/balking/etc/balking.ucls b/balking/etc/balking.ucls
new file mode 100644
index 000000000000..e750f888a9ef
--- /dev/null
+++ b/balking/etc/balking.ucls
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/balking/etc/balking.urm.puml b/balking/etc/balking.urm.puml
new file mode 100644
index 000000000000..191fd350bcd8
--- /dev/null
+++ b/balking/etc/balking.urm.puml
@@ -0,0 +1,30 @@
+@startuml
+package com.iluwatar.balking {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ + main(args : String[]) {static}
+ }
+ interface DelayProvider {
+ + executeAfterDelay(long, TimeUnit, Runnable) {abstract}
+ }
+ class WashingMachine {
+ - LOGGER : Logger {static}
+ - delayProvider : DelayProvider
+ - washingMachineState : WashingMachineState
+ + WashingMachine()
+ + WashingMachine(delayProvider : DelayProvider)
+ + endOfWashing()
+ + getWashingMachineState() : WashingMachineState
+ + wash()
+ }
+ enum WashingMachineState {
+ + ENABLED {static}
+ + WASHING {static}
+ + valueOf(name : String) : WashingMachineState {static}
+ + values() : WashingMachineState[] {static}
+ }
+}
+WashingMachine --> "-washingMachineState" WashingMachineState
+WashingMachine --> "-delayProvider" DelayProvider
+@enduml
\ No newline at end of file
diff --git a/balking/pom.xml b/balking/pom.xml
new file mode 100644
index 000000000000..818f7549c330
--- /dev/null
+++ b/balking/pom.xml
@@ -0,0 +1,70 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ balking
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.balking.App
+
+
+
+
+
+
+
+
+
diff --git a/balking/src/main/java/com/iluwatar/balking/App.java b/balking/src/main/java/com/iluwatar/balking/App.java
new file mode 100644
index 000000000000..c7651ec54147
--- /dev/null
+++ b/balking/src/main/java/com/iluwatar/balking/App.java
@@ -0,0 +1,66 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.balking;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * In Balking Design Pattern if an object’s method is invoked when it is in an inappropriate state,
+ * then the method will return without doing anything. Objects that use this pattern are generally
+ * only in a state that is prone to balking temporarily but for an unknown amount of time
+ *
+ * In this example implementation, {@link WashingMachine} is an object that has two states in
+ * which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING
+ * using a thread-safe method. On the other hand, if it already has been washing and any other
+ * thread executes {@link WashingMachine#wash()} it won't do that and returns without doing
+ * anything.
+ */
+@Slf4j
+public class App {
+
+ /**
+ * Entry Point.
+ *
+ * @param args the command line arguments - not used
+ */
+ public static void main(String... args) {
+ final var washingMachine = new WashingMachine();
+ var executorService = Executors.newFixedThreadPool(3);
+ for (int i = 0; i < 3; i++) {
+ executorService.execute(washingMachine::wash);
+ }
+ executorService.shutdown();
+ try {
+ if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
+ executorService.shutdownNow();
+ }
+ } catch (InterruptedException ie) {
+ LOGGER.error("ERROR: Waiting on executor service shutdown!");
+ Thread.currentThread().interrupt();
+ }
+ }
+}
diff --git a/balking/src/main/java/com/iluwatar/balking/DelayProvider.java b/balking/src/main/java/com/iluwatar/balking/DelayProvider.java
new file mode 100644
index 000000000000..f27922219ea6
--- /dev/null
+++ b/balking/src/main/java/com/iluwatar/balking/DelayProvider.java
@@ -0,0 +1,32 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.balking;
+
+import java.util.concurrent.TimeUnit;
+
+/** An interface to simulate delay while executing some work. */
+public interface DelayProvider {
+ void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
+}
diff --git a/balking/src/main/java/com/iluwatar/balking/WashingMachine.java b/balking/src/main/java/com/iluwatar/balking/WashingMachine.java
new file mode 100644
index 000000000000..52ce7c593b6b
--- /dev/null
+++ b/balking/src/main/java/com/iluwatar/balking/WashingMachine.java
@@ -0,0 +1,83 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.balking;
+
+import java.util.concurrent.TimeUnit;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+/** Washing machine class. */
+@Slf4j
+public class WashingMachine {
+
+ private final DelayProvider delayProvider;
+
+ @Getter private WashingMachineState washingMachineState;
+
+ /** Creates a new instance of WashingMachine. */
+ public WashingMachine() {
+ this(
+ (interval, timeUnit, task) -> {
+ try {
+ Thread.sleep(timeUnit.toMillis(interval));
+ } catch (InterruptedException ie) {
+ LOGGER.error("", ie);
+ Thread.currentThread().interrupt();
+ }
+ task.run();
+ });
+ }
+
+ /**
+ * Creates a new instance of WashingMachine using provided delayProvider. This constructor is used
+ * only for unit testing purposes.
+ */
+ public WashingMachine(DelayProvider delayProvider) {
+ this.delayProvider = delayProvider;
+ this.washingMachineState = WashingMachineState.ENABLED;
+ }
+
+ /** Method responsible for washing if the object is in appropriate state. */
+ public void wash() {
+ synchronized (this) {
+ var machineState = getWashingMachineState();
+ LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState);
+ if (this.washingMachineState == WashingMachineState.WASHING) {
+ LOGGER.error("Cannot wash if the machine has been already washing!");
+ return;
+ }
+ this.washingMachineState = WashingMachineState.WASHING;
+ }
+ LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
+
+ this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
+ }
+
+ /** Method is responsible for ending the washing by changing machine state. */
+ public synchronized void endOfWashing() {
+ washingMachineState = WashingMachineState.ENABLED;
+ LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
+ }
+}
diff --git a/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java b/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java
new file mode 100644
index 000000000000..e0b2ba7ac8f1
--- /dev/null
+++ b/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java
@@ -0,0 +1,34 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.balking;
+
+/**
+ * WashingMachineState enum describes in which state machine is, it can be enabled and ready to work
+ * as well as during washing.
+ */
+public enum WashingMachineState {
+ ENABLED,
+ WASHING
+}
diff --git a/balking/src/test/java/com/iluwatar/balking/AppTest.java b/balking/src/test/java/com/iluwatar/balking/AppTest.java
new file mode 100644
index 000000000000..40beabf553d0
--- /dev/null
+++ b/balking/src/test/java/com/iluwatar/balking/AppTest.java
@@ -0,0 +1,45 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.balking;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+
+/** Application 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} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow((Executable) App::main);
+ }
+}
diff --git a/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java b/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
new file mode 100644
index 000000000000..9bf7ac2548a0
--- /dev/null
+++ b/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
@@ -0,0 +1,70 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.balking;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.concurrent.TimeUnit;
+import org.junit.jupiter.api.Test;
+
+/** Tests for {@link WashingMachine} */
+class WashingMachineTest {
+
+ private final FakeDelayProvider fakeDelayProvider = new FakeDelayProvider();
+
+ @Test
+ void wash() {
+ var washingMachine = new WashingMachine(fakeDelayProvider);
+
+ washingMachine.wash();
+ washingMachine.wash();
+
+ var machineStateGlobal = washingMachine.getWashingMachineState();
+
+ fakeDelayProvider.task.run();
+
+ // washing machine remains in washing state
+ assertEquals(WashingMachineState.WASHING, machineStateGlobal);
+
+ // washing machine goes back to enabled state
+ assertEquals(WashingMachineState.ENABLED, washingMachine.getWashingMachineState());
+ }
+
+ @Test
+ void endOfWashing() {
+ var washingMachine = new WashingMachine();
+ washingMachine.wash();
+ assertEquals(WashingMachineState.ENABLED, washingMachine.getWashingMachineState());
+ }
+
+ private static class FakeDelayProvider implements DelayProvider {
+ private Runnable task;
+
+ @Override
+ public void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task) {
+ this.task = task;
+ }
+ }
+}
diff --git a/bloc/README.md b/bloc/README.md
new file mode 100644
index 000000000000..76a4a0917485
--- /dev/null
+++ b/bloc/README.md
@@ -0,0 +1,230 @@
+---
+title: "Bloc Pattern in Java: State Management Simplified"
+shortTitle: Bloc
+description: "Learn how the Bloc pattern helps manage state changes in Java applications. This guide covers dynamic listener management, real-world examples, and clean code practices for state management."
+category: Architectural
+language: en
+tag:
+ - Abstraction
+ - Data binding
+ - Decoupling
+ - Event-driven
+ - Presentation
+ - Reactive
+ - Reusability
+ - State tracking
+---
+
+## Also known as
+
+* Business Logic Component
+* Business Logic Controller
+
+## Intent of the Bloc Pattern
+
+The Bloc pattern manages the state of an object and allows for dynamically notifying interested listeners about state changes. It separates state management logic from the rest of the application, improving code organization and flexibility.
+
+## Detailed explanation of the Bloc pattern with real-World examples
+
+Real-world example
+
+> Consider a digital counter application where multiple parts of the UI need to be updated whenever the counter changes. For example, a label displaying the counter value and an activity log showing changes. Instead of directly modifying these UI components, the Bloc pattern manages the counter state and notifies all registered listeners about the state change. Listeners can dynamically subscribe or unsubscribe from receiving updates.
+
+In plain words
+
+> The Bloc pattern manages a single state object and dynamically notifies registered listeners whenever the state changes.
+
+Wikipedia says
+
+> While not a formalized "Gang of Four" design pattern, Bloc is widely used in state-driven applications. It centralizes state management and propagates state changes to registered observers, following principles of separation of concerns.
+
+Sequence diagram
+
+
+
+## Programmatic Example of the Bloc Pattern in Java
+
+This example demonstrates how to implement the Bloc pattern using Java and Swing. The pattern separates the state of the application from UI components, and provides a reactive, event-driven approach to managing updates.
+
+Core components of the Bloc Pattern include a `State` object, a `Bloc` class responsible for managing and updating that state, and interfaces (`StateListener` and `ListenerManager`) for subscribing to changes.
+
+The `State` class represents the application's data at any given time.
+
+```java
+public record State(int value) {}
+```
+
+The `ListenerManager` interface declares methods to add and remove listeners, as well as retrieve them.
+
+```java
+public interface ListenerManager {
+ void addListener(StateListener listener);
+ void removeListener(StateListener listener);
+ List> getListeners();
+}
+```
+
+The `StateListener` interface defines how a listener reacts to state changes.
+
+```java
+public interface StateListener {
+ void onStateChange(T state);
+}
+```
+
+The `Bloc` class maintains the current state and notifies listeners of updates. It provides `increment` and `decrement` methods to update state and automatically notify registered listeners.
+
+```java
+public class Bloc implements ListenerManager {
+
+ private State currentState;
+ private final List> listeners = new ArrayList<>();
+
+ public Bloc() {
+ this.currentState = new State(0);
+ }
+
+ @Override
+ public void addListener(StateListener listener) {
+ listeners.add(listener);
+ listener.onStateChange(currentState);
+ }
+
+ @Override
+ public void removeListener(StateListener listener) {
+ listeners.remove(listener);
+ }
+
+ @Override
+ public List> getListeners() {
+ return Collections.unmodifiableList(listeners);
+ }
+
+ private void emitState(State newState) {
+ currentState = newState;
+ for (StateListener listener : listeners) {
+ listener.onStateChange(currentState);
+ }
+ }
+
+ public void increment() {
+ emitState(new State(currentState.value() + 1));
+ }
+
+ public void decrement() {
+ emitState(new State(currentState.value() - 1));
+ }
+}
+```
+
+This class demonstrates how to integrate the Bloc pattern with a simple Swing GUI. It sets up a counter, buttons to change the state, and a toggle to enable or disable the listener dynamically.
+
+```java
+public class Main {
+ public static void main(String[] args) {
+ BlocUi blocUi = new BlocUi();
+ blocUi.createAndShowUi();
+ }
+}
+
+public class BlocUi {
+
+ public void createAndShowUi() {
+ final Bloc bloc = new Bloc();
+
+ JFrame frame = new JFrame("BloC example");
+ frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ frame.setSize(400, 300);
+
+ JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER);
+ counterLabel.setFont(new Font("Arial", Font.BOLD, 20));
+
+ JButton decrementButton = new JButton("Decrement");
+ JButton toggleListenerButton = new JButton("Disable Listener");
+ JButton incrementButton = new JButton("Increment");
+
+ frame.setLayout(new BorderLayout());
+ frame.add(counterLabel, BorderLayout.CENTER);
+ frame.add(incrementButton, BorderLayout.NORTH);
+ frame.add(decrementButton, BorderLayout.SOUTH);
+ frame.add(toggleListenerButton, BorderLayout.EAST);
+
+ StateListener stateListener = state -> counterLabel.setText("Counter: " + state.value());
+
+ bloc.addListener(stateListener);
+
+ toggleListenerButton.addActionListener(
+ e -> {
+ if (bloc.getListeners().contains(stateListener)) {
+ bloc.removeListener(stateListener);
+ toggleListenerButton.setText("Enable Listener");
+ } else {
+ bloc.addListener(stateListener);
+ toggleListenerButton.setText("Disable Listener");
+ }
+ });
+
+ incrementButton.addActionListener(e -> bloc.increment());
+ decrementButton.addActionListener(e -> bloc.decrement());
+
+ frame.setVisible(true);
+ }
+}
+```
+
+### Program Output
+
+- **On Increment**
+ `Counter: 1`
+
+- **On Decrement**
+ `Counter: 0`
+
+- **Dynamic Listener Toggle**
+ - Listener disabled: Counter stops updating.
+ - Listener enabled: Counter updates again.
+
+## When to Use the Bloc Pattern
+
+Use the Bloc pattern when:
+
+* When you want a clean separation of business logic and UI in Java applications
+* When you need a reactive approach to updating UI based on state changes
+* When you want to avoid coupling controllers or presenters directly to data manipulation
+* When multiple UI elements need access to the same business logic
+
+## Real-World Applications of Bloc Pattern
+
+* Java-based desktop applications that require real-time UI updates
+* Backend-driven Java frameworks that separate service layers from presentation
+* Cross-platform applications where the logic must remain consistent regardless of the UI technology
+
+## Benefits and Trade-offs of Bloc Pattern
+
+Benefits:
+
+* Simplifies UI components by removing direct business logic
+* Improves testability by isolating state and behavior
+* Encourages code reuse by centralizing data flows
+* Enhances maintainability through clear separation of concerns
+
+Trade-offs:
+
+* May introduce additional boilerplate code for managing streams or observers
+* Requires careful design to avoid a monolithic “god” component
+* Demands consistent reactive programming practices to be effective
+
+## Related Patterns
+
+- [Observer](https://java-design-patterns.com/patterns/observer/): Bloc is a specialized implementation of the Observer pattern.
+- [Mediator](https://java-design-patterns.com/patterns/mediator/): Orchestrates interactions among multiple objects through a central component
+- [MVC](https://java-design-patterns.com/patterns/model-view-controller/): Shares the idea of separating concerns between layers
+
+## References and Credits
+
+* [Bloc architecture(bloclibrary.dev)](https://bloclibrary.dev/architecture/)
+* [Clean Architecture: A Craftsman's Guide to Software Structure and Design](https://amzn.to/3UoKkaR)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Event-Driven Programming in Java (Oracle)](https://www.oracle.com/java/)
+* [Java Swing Documentation (Oracle)](https://docs.oracle.com/javase/tutorial/uiswing/)
diff --git a/bloc/etc/bloc-sequence-diagram.png b/bloc/etc/bloc-sequence-diagram.png
new file mode 100644
index 000000000000..588863985019
Binary files /dev/null and b/bloc/etc/bloc-sequence-diagram.png differ
diff --git a/bloc/etc/bloc.png b/bloc/etc/bloc.png
new file mode 100644
index 000000000000..60d6eb77c8fc
Binary files /dev/null and b/bloc/etc/bloc.png differ
diff --git a/bloc/etc/bloc.puml b/bloc/etc/bloc.puml
new file mode 100644
index 000000000000..5991f533ae70
--- /dev/null
+++ b/bloc/etc/bloc.puml
@@ -0,0 +1,41 @@
+@startuml
+package com.iluwatar.bloc {
+
+ class State {
+ - value : int
+ + State(value : int)
+ + getValue() : int
+ }
+
+ interface StateListener {
+ + onStateChange(state : T)
+ }
+
+ interface ListenerManager {
+ + addListener(listener : StateListener)
+ + removeListener(listener : StateListener)
+ + getListeners() : List>
+ }
+
+ class BloC {
+ - currentState : State
+ - listeners : List>
+ + BloC()
+ + addListener(listener : StateListener)
+ + removeListener(listener : StateListener)
+ + getListeners() : List>
+ - emitState(newState : State)
+ + increment()
+ + decrement()
+ }
+
+ class Main {
+ + main(args : String[])
+ }
+
+ ListenerManager <|.. BloC
+ StateListener <|.. BloC
+ BloC o-- State
+ BloC *-- StateListener
+}
+@enduml
diff --git a/bloc/etc/bloc.urm.puml b/bloc/etc/bloc.urm.puml
new file mode 100644
index 000000000000..6408aa76e6a4
--- /dev/null
+++ b/bloc/etc/bloc.urm.puml
@@ -0,0 +1,32 @@
+@startuml
+package com.iluwatar.bloc {
+ class Bloc {
+ - currentState : State
+ - listeners : List>
+ + Bloc()
+ + addListener(listener : StateListener)
+ + decrement()
+ - emitState(newState : State)
+ + getListeners() : List>
+ + increment()
+ + removeListener(listener : StateListener)
+ }
+ class BlocUi {
+ + BlocUi()
+ + createAndShowUi()
+ }
+ interface ListenerManager {
+ + addListener(StateListener) {abstract}
+ + getListeners() : List> {abstract}
+ + removeListener(StateListener) {abstract}
+ }
+ class Main {
+ + Main()
+ + main(args : String[]) {static}
+ }
+ interface StateListener {
+ + onStateChange(T) {abstract}
+ }
+}
+Bloc ..|> ListenerManager
+@enduml
\ No newline at end of file
diff --git a/bloc/pom.xml b/bloc/pom.xml
new file mode 100644
index 000000000000..cc52a3b99dc2
--- /dev/null
+++ b/bloc/pom.xml
@@ -0,0 +1,76 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ bloc
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.testng
+ testng
+ 7.11.0
+ test
+
+
+ org.assertj
+ assertj-core
+ 3.27.3
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.bloc.Main
+
+
+
+
+
+
+
+
+
diff --git a/bloc/src/main/java/com/iluwatar/bloc/Bloc.java b/bloc/src/main/java/com/iluwatar/bloc/Bloc.java
new file mode 100644
index 000000000000..f6ab0a61cdbf
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/Bloc.java
@@ -0,0 +1,98 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bloc;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The Bloc class is responsible for managing the current state and notifying registered listeners
+ * whenever the state changes. It implements the ListenerManager interface, allowing listeners to be
+ * added, removed, and notified of state changes.
+ */
+public class Bloc implements ListenerManager {
+
+ private State currentState;
+ private final List> listeners = new ArrayList<>();
+
+ /** Constructs a new Bloc instance with an initial state of value 0. */
+ public Bloc() {
+ this.currentState = new State(0);
+ }
+
+ /**
+ * Adds a listener to receive state change notifications.
+ *
+ * @param listener the listener to add
+ */
+ @Override
+ public void addListener(StateListener listener) {
+ listeners.add(listener);
+ listener.onStateChange(currentState);
+ }
+
+ /**
+ * Removes a listener from receiving state change notifications.
+ *
+ * @param listener the listener to remove
+ */
+ @Override
+ public void removeListener(StateListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Returns an unmodifiable list of all registered listeners.
+ *
+ * @return an unmodifiable list of listeners
+ */
+ @Override
+ public List> getListeners() {
+ return Collections.unmodifiableList(listeners);
+ }
+
+ /**
+ * Emits a new state and notifies all registered listeners of the change.
+ *
+ * @param newState the new state to emit
+ */
+ private void emitState(State newState) {
+ currentState = newState;
+ for (StateListener listener : listeners) {
+ listener.onStateChange(currentState);
+ }
+ }
+
+ /** Increments the current state value by 1 and notifies listeners of the change. */
+ public void increment() {
+ emitState(new State(currentState.value() + 1));
+ }
+
+ /** Decrements the current state value by 1 and notifies listeners of the change. */
+ public void decrement() {
+ emitState(new State(currentState.value() - 1));
+ }
+}
diff --git a/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java b/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java
new file mode 100644
index 000000000000..500d455d82e1
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java
@@ -0,0 +1,85 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bloc;
+
+import java.awt.BorderLayout;
+import java.awt.Font;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.SwingConstants;
+import javax.swing.WindowConstants;
+
+/** The BlocUI class handles the creation and management of the UI components. */
+public class BlocUi {
+
+ /** Creates and shows the UI. */
+ public void createAndShowUi() {
+ // Create a Bloc instance to manage the state
+ final Bloc bloc = new Bloc();
+
+ // setting up a frame window with a title
+ JFrame frame = new JFrame("BloC example");
+ frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ frame.setSize(400, 300);
+
+ // label to display the counter value
+ JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER);
+ counterLabel.setFont(new Font("Arial", Font.BOLD, 20));
+
+ // buttons for increment, decrement, and toggling listener
+ JButton decrementButton = new JButton("Decrement");
+ JButton toggleListenerButton = new JButton("Disable Listener");
+ JButton incrementButton = new JButton("Increment");
+
+ frame.setLayout(new BorderLayout());
+ frame.add(counterLabel, BorderLayout.CENTER);
+ frame.add(incrementButton, BorderLayout.NORTH);
+ frame.add(decrementButton, BorderLayout.SOUTH);
+ frame.add(toggleListenerButton, BorderLayout.EAST);
+
+ // making a state listener to update the counter label when the state changes
+ StateListener stateListener = state -> counterLabel.setText("Counter: " + state.value());
+
+ // adding the listener to the Bloc instance
+ bloc.addListener(stateListener);
+
+ toggleListenerButton.addActionListener(
+ e -> {
+ if (bloc.getListeners().contains(stateListener)) {
+ bloc.removeListener(stateListener);
+ toggleListenerButton.setText("Enable Listener");
+ } else {
+ bloc.addListener(stateListener);
+ toggleListenerButton.setText("Disable Listener");
+ }
+ });
+
+ incrementButton.addActionListener(e -> bloc.increment());
+ decrementButton.addActionListener(e -> bloc.decrement());
+
+ frame.setVisible(true);
+ }
+}
diff --git a/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java b/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java
new file mode 100644
index 000000000000..cd55b0fb320d
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java
@@ -0,0 +1,56 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bloc;
+
+import java.util.List;
+
+/**
+ * Interface for managing listeners for state changes.
+ *
+ * @param The type of state to be handled by the listeners.
+ */
+public interface ListenerManager {
+
+ /**
+ * Adds a listener that will be notified of state changes.
+ *
+ * @param listener the listener to be added
+ */
+ void addListener(StateListener listener);
+
+ /**
+ * Removes a listener so that it no longer receives state change notifications.
+ *
+ * @param listener the listener to be removed
+ */
+ void removeListener(StateListener listener);
+
+ /**
+ * Returns a list of all listeners currently registered for state changes.
+ *
+ * @return a list of registered listeners
+ */
+ List> getListeners();
+}
diff --git a/bloc/src/main/java/com/iluwatar/bloc/Main.java b/bloc/src/main/java/com/iluwatar/bloc/Main.java
new file mode 100644
index 000000000000..b7a929bcf2bd
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/Main.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bloc;
+
+/**
+ * The BLoC (Business Logic Component) pattern is a software design pattern primarily used in
+ * Flutter applications. It facilitates the separation of business logic from UI code, making the
+ * application more modular, testable, and scalable. The BLoC pattern uses streams to manage the
+ * flow of data and state changes, allowing widgets to react to new states as they arrive. In the
+ * BLoC pattern, the application is divided into three key components: - Input streams: Represent
+ * user interactions or external events fed into the BLoC. - Business logic: Processes the input and
+ * determines the resulting state or actions. - Output streams: Emit the updated state for the UI to
+ * consume. The BLoC pattern is especially useful in reactive programming scenarios and aligns well
+ * with the declarative nature of Flutter. By using this pattern, developers can ensure a clear
+ * separation of concerns, enhance reusability, and maintain consistent state management throughout
+ * the application.
+ */
+public class Main {
+
+ /**
+ * The entry point of the application. Initializes the GUI.
+ *
+ * @param args command-line arguments (not used in this example)
+ */
+ public static void main(String[] args) {
+ BlocUi blocUi = new BlocUi();
+ blocUi.createAndShowUi();
+ }
+}
diff --git a/bloc/src/main/java/com/iluwatar/bloc/State.java b/bloc/src/main/java/com/iluwatar/bloc/State.java
new file mode 100644
index 000000000000..430747548cd3
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/State.java
@@ -0,0 +1,31 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bloc;
+
+/**
+ * The {@code State} class represents a state with an integer value. This class encapsulates the
+ * value and provides methods to retrieve it.
+ */
+public record State(int value) {}
diff --git a/bloc/src/main/java/com/iluwatar/bloc/StateListener.java b/bloc/src/main/java/com/iluwatar/bloc/StateListener.java
new file mode 100644
index 000000000000..77aac172e4e3
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/StateListener.java
@@ -0,0 +1,42 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bloc;
+
+/**
+ * The {@code StateListener} interface defines the contract for listening to state changes.
+ * Implementations of this interface should handle state changes and define actions to take when the
+ * state changes.
+ *
+ * @param the type of state that this listener will handle
+ */
+public interface StateListener {
+
+ /**
+ * This method is called when the state has changed.
+ *
+ * @param state the updated state
+ */
+ void onStateChange(T state);
+}
diff --git a/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java
new file mode 100644
index 000000000000..98e34b8d4a22
--- /dev/null
+++ b/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java
@@ -0,0 +1,85 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bloc;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class BlocTest {
+ private Bloc bloc;
+ private AtomicInteger stateValue;
+
+ @BeforeEach
+ void setUp() {
+ bloc = new Bloc();
+ stateValue = new AtomicInteger(0);
+ }
+
+ @Test
+ void initialState() {
+ assertTrue(bloc.getListeners().isEmpty(), "No listeners should be present initially.");
+ }
+
+ @Test
+ void IncrementUpdateState() {
+ bloc.addListener(state -> stateValue.set(state.value()));
+ bloc.increment();
+ assertEquals(1, stateValue.get(), "State should increment to 1");
+ }
+
+ @Test
+ void DecrementUpdateState() {
+ bloc.addListener(state -> stateValue.set(state.value()));
+ bloc.decrement();
+ assertEquals(-1, stateValue.get(), "State should decrement to -1");
+ }
+
+ @Test
+ void addingListener() {
+ bloc.addListener(state -> {});
+ assertEquals(1, bloc.getListeners().size(), "Listener count should be 1.");
+ }
+
+ @Test
+ void removingListener() {
+ StateListener listener = state -> {};
+ bloc.addListener(listener);
+ bloc.removeListener(listener);
+ assertTrue(bloc.getListeners().isEmpty(), "Listener count should be 0 after removal.");
+ }
+
+ @Test
+ void multipleListeners() {
+ AtomicInteger secondValue = new AtomicInteger();
+ bloc.addListener(state -> stateValue.set(state.value()));
+ bloc.addListener(state -> secondValue.set(state.value()));
+ bloc.increment();
+ assertEquals(1, stateValue.get(), "First listener should receive state 1.");
+ assertEquals(1, secondValue.get(), "Second listener should receive state 1.");
+ }
+}
diff --git a/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java
new file mode 100644
index 000000000000..f1fc73947896
--- /dev/null
+++ b/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java
@@ -0,0 +1,121 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bloc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.awt.*;
+import javax.swing.*;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class BlocUiTest {
+
+ private JFrame frame;
+ private JLabel counterLabel;
+ private JButton incrementButton;
+ private JButton decrementButton;
+ private JButton toggleListenerButton;
+ private Bloc bloc;
+ private StateListener stateListener;
+
+ @BeforeEach
+ void setUp() {
+ bloc = new Bloc(); // Re-initialize the Bloc for each test
+
+ frame = new JFrame("BloC example");
+ frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ frame.setSize(400, 300);
+
+ counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER);
+ counterLabel.setFont(new Font("Arial", Font.BOLD, 20));
+
+ incrementButton = new JButton("Increment");
+ decrementButton = new JButton("Decrement");
+ toggleListenerButton = new JButton("Disable Listener");
+
+ frame.setLayout(new BorderLayout());
+ frame.add(counterLabel, BorderLayout.CENTER);
+ frame.add(incrementButton, BorderLayout.NORTH);
+ frame.add(decrementButton, BorderLayout.SOUTH);
+ frame.add(toggleListenerButton, BorderLayout.EAST);
+
+ stateListener = state -> counterLabel.setText("Counter: " + state.value());
+ bloc.addListener(stateListener);
+
+ incrementButton.addActionListener(e -> bloc.increment());
+ decrementButton.addActionListener(e -> bloc.decrement());
+ toggleListenerButton.addActionListener(
+ e -> {
+ if (bloc.getListeners().contains(stateListener)) {
+ bloc.removeListener(stateListener);
+ toggleListenerButton.setText("Enable Listener");
+ } else {
+ bloc.addListener(stateListener);
+ toggleListenerButton.setText("Disable Listener");
+ }
+ });
+
+ frame.setVisible(true);
+ }
+
+ @AfterEach
+ void tearDown() {
+ frame.dispose();
+ bloc = new Bloc(); // Reset Bloc state after each test to avoid state carryover
+ }
+
+ @Test
+ void testIncrementButton() {
+ simulateButtonClick(incrementButton);
+ assertEquals("Counter: 1", counterLabel.getText());
+ }
+
+ @Test
+ void testDecrementButton() {
+ simulateButtonClick(decrementButton);
+ assertEquals("Counter: -1", counterLabel.getText());
+ }
+
+ @Test
+ void testToggleListenerButton() {
+ // Disable listener
+ simulateButtonClick(toggleListenerButton);
+ simulateButtonClick(incrementButton);
+ assertEquals("Counter: 0", counterLabel.getText()); // Listener is disabled
+
+ // Enable listener
+ simulateButtonClick(toggleListenerButton);
+ simulateButtonClick(incrementButton);
+ assertEquals("Counter: 2", counterLabel.getText()); // Listener is re-enabled
+ }
+
+ private void simulateButtonClick(JButton button) {
+ for (var listener : button.getActionListeners()) {
+ listener.actionPerformed(null);
+ }
+ }
+}
diff --git a/bridge/README.md b/bridge/README.md
new file mode 100644
index 000000000000..26567b292c81
--- /dev/null
+++ b/bridge/README.md
@@ -0,0 +1,256 @@
+---
+title: "Bridge Pattern in Java: Decouple Abstraction from Implementation"
+shortTitle: Bridge
+description: "Learn about the Bridge design pattern in Java. Decouple abstraction from implementation to enhance flexibility and extensibility. Explore real-world examples, class diagrams, and use cases."
+category: Structural
+language: en
+tag:
+ - Abstraction
+ - Decoupling
+ - Extensibility
+ - Gang of Four
+ - Object composition
+---
+
+## Also known as
+
+* Handle/Body
+
+## Intent of Bridge Design Pattern
+
+The Bridge design pattern is a structural pattern in Java that decouples an abstraction from its implementation, allowing both to vary independently. This pattern is essential for developing flexible and extensible software systems.
+
+## Detailed Explanation of Bridge Pattern with Real-World Examples
+
+Real-world example
+
+> In Java, the Bridge pattern is commonly used in GUI frameworks, database drivers, and device drivers. For instance, a universal remote control (abstraction) can operate various TV brands (implementations) through a consistent interface.
+>
+> Imagine a universal remote control (abstraction) that can operate different brands and types of televisions (implementations). The remote control provides a consistent interface for operations like turning on/off, changing channels, and adjusting the volume. Each television brand or type has its own specific implementation of these operations. By using the Bridge pattern, the remote control interface is decoupled from the television implementations, allowing the remote control to work with any television regardless of its brand or internal workings. This separation allows new television models to be added without changing the remote control's code, and different remote controls can be developed to work with the same set of televisions.
+
+In Plain Words
+
+> Bridge pattern is about preferring composition to inheritance. Implementation details are pushed from a hierarchy to another object with a separate hierarchy.
+
+Wikipedia says
+
+> The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently"
+
+Sequence diagram
+
+
+
+## Programmatic Example of Bridge Pattern in Java
+
+Imagine you have a weapon that can have various enchantments, and you need to combine different weapons with different enchantments. How would you handle this? Would you create multiple copies of each weapon, each with a different enchantment, or would you create separate enchantments and apply them to the weapon as needed? The Bridge pattern enables you to do the latter.
+
+Here we have the `Weapon` hierarchy:
+
+```java
+public interface Weapon {
+ void wield();
+
+ void swing();
+
+ void unwield();
+
+ Enchantment getEnchantment();
+}
+
+public class Sword implements Weapon {
+
+ private final Enchantment enchantment;
+
+ public Sword(Enchantment enchantment) {
+ this.enchantment = enchantment;
+ }
+
+ @Override
+ public void wield() {
+ LOGGER.info("The sword is wielded.");
+ enchantment.onActivate();
+ }
+
+ @Override
+ public void swing() {
+ LOGGER.info("The sword is swung.");
+ enchantment.apply();
+ }
+
+ @Override
+ public void unwield() {
+ LOGGER.info("The sword is unwielded.");
+ enchantment.onDeactivate();
+ }
+
+ @Override
+ public Enchantment getEnchantment() {
+ return enchantment;
+ }
+}
+
+public class Hammer implements Weapon {
+
+ private final Enchantment enchantment;
+
+ public Hammer(Enchantment enchantment) {
+ this.enchantment = enchantment;
+ }
+
+ @Override
+ public void wield() {
+ LOGGER.info("The hammer is wielded.");
+ enchantment.onActivate();
+ }
+
+ @Override
+ public void swing() {
+ LOGGER.info("The hammer is swung.");
+ enchantment.apply();
+ }
+
+ @Override
+ public void unwield() {
+ LOGGER.info("The hammer is unwielded.");
+ enchantment.onDeactivate();
+ }
+
+ @Override
+ public Enchantment getEnchantment() {
+ return enchantment;
+ }
+}
+```
+
+Here's the separate `Enchantment` hierarchy:
+
+```java
+public interface Enchantment {
+ void onActivate();
+
+ void apply();
+
+ void onDeactivate();
+}
+
+public class FlyingEnchantment implements Enchantment {
+
+ @Override
+ public void onActivate() {
+ LOGGER.info("The item begins to glow faintly.");
+ }
+
+ @Override
+ public void apply() {
+ LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
+ }
+
+ @Override
+ public void onDeactivate() {
+ LOGGER.info("The item's glow fades.");
+ }
+}
+
+public class SoulEatingEnchantment implements Enchantment {
+
+ @Override
+ public void onActivate() {
+ LOGGER.info("The item spreads bloodlust.");
+ }
+
+ @Override
+ public void apply() {
+ LOGGER.info("The item eats the soul of enemies.");
+ }
+
+ @Override
+ public void onDeactivate() {
+ LOGGER.info("Bloodlust slowly disappears.");
+ }
+}
+```
+
+Here are both hierarchies in action:
+
+```java
+public static void main(String[] args) {
+ LOGGER.info("The knight receives an enchanted sword.");
+ var enchantedSword = new Sword(new SoulEatingEnchantment());
+ enchantedSword.wield();
+ enchantedSword.swing();
+ enchantedSword.unwield();
+
+ LOGGER.info("The valkyrie receives an enchanted hammer.");
+ var hammer = new Hammer(new FlyingEnchantment());
+ hammer.wield();
+ hammer.swing();
+ hammer.unwield();
+}
+```
+
+Here's the console output.
+
+```
+The knight receives an enchanted sword.
+The sword is wielded.
+The item spreads bloodlust.
+The sword is swung.
+The item eats the soul of enemies.
+The sword is unwielded.
+Bloodlust slowly disappears.
+The valkyrie receives an enchanted hammer.
+The hammer is wielded.
+The item begins to glow faintly.
+The hammer is swung.
+The item flies and strikes the enemies finally returning to owner's hand.
+The hammer is unwielded.
+The item's glow fades.
+```
+
+## When to Use the Bridge Pattern in Java
+
+Consider using the Bridge pattern when:
+
+* You need to avoid a permanent binding between an abstraction and its implementation, such as when the implementation must be chosen or switched at runtime.
+* Both the abstractions and their implementations should be extendable via subclassing, allowing independent extension of each component.
+* Changes to the implementation of an abstraction should not affect clients, meaning their code should not require recompilation.
+* You encounter a large number of classes in your hierarchy, indicating the need to split an object into two parts, a concept referred to as "nested generalizations" by Rumbaugh.
+* You want to share an implementation among multiple objects, potentially using reference counting, while keeping this detail hidden from the client, as exemplified by Coplien's String class, where multiple objects can share the same string representation.
+
+## Bridge Pattern Java Tutorials
+
+* [Bridge Pattern Tutorial (DigitalOcean)](https://www.digitalocean.com/community/tutorials/bridge-design-pattern-java)
+
+## Real-World Applications of Bridge Pattern in Java
+
+* GUI Frameworks where the abstraction is the window, and the implementation could be the underlying OS windowing system.
+* Database Drivers where the abstraction is a generic database interface, and the implementations are database-specific drivers.
+* Device Drivers where the abstraction is the device-independent code, and the implementation is the device-dependent code.
+
+## Benefits and Trade-offs of Bridge Pattern
+
+Benefits:
+
+* Decoupling Interface and Implementation: The Bridge pattern enhances modularity by separating the interface (the high-level operations) from the implementation (the low-level operations).
+* Improved Extensibility: You can extend the abstraction and implementation hierarchies independently.
+* Hiding Implementation Details: Clients only see the abstraction's interface, not its implementation.
+
+Trade-offs:
+
+* Increased Complexity: The pattern can complicate the system architecture and code, especially for clients unfamiliar with the pattern.
+* Runtime Overhead: The extra layer of abstraction can introduce a performance penalty, although it is often negligible in practice.
+
+## Related Java Design Patterns
+
+* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): The Abstract Factory pattern can be used along with the Bridge pattern to create platforms that are independent of the concrete classes used to create their objects.
+* [Adapter](https://java-design-patterns.com/patterns/adapter/): The Adapter pattern is used to provide a different interface to an object, while the Bridge pattern is used to separate an object's interface from its implementation.
+* [Composite](https://java-design-patterns.com/patterns/composite/): The Bridge pattern is often used with the Composite pattern to model the implementation details of a component.
+* [Strategy](https://java-design-patterns.com/patterns/strategy/): The Strategy pattern is like the Bridge pattern, but with a different intent. Both patterns are based on composition: Strategy uses composition to change the behavior of a class, while Bridge uses composition to separate an abstraction from its implementation.
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
+* [Pattern-Oriented Software Architecture Volume 1: A System of Patterns](https://amzn.to/3TEnhtl)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
diff --git a/bridge/etc/bridge-sequence-diagram.png b/bridge/etc/bridge-sequence-diagram.png
new file mode 100644
index 000000000000..b797827628bc
Binary files /dev/null and b/bridge/etc/bridge-sequence-diagram.png differ
diff --git a/bridge/etc/bridge.png b/bridge/etc/bridge.png
deleted file mode 100644
index 472fe51e74f5..000000000000
Binary files a/bridge/etc/bridge.png and /dev/null differ
diff --git a/bridge/etc/bridge.ucls b/bridge/etc/bridge.ucls
deleted file mode 100644
index 86455e7fb1d8..000000000000
--- a/bridge/etc/bridge.ucls
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bridge/etc/bridge.urm.png b/bridge/etc/bridge.urm.png
new file mode 100644
index 000000000000..785585bf8163
Binary files /dev/null and b/bridge/etc/bridge.urm.png differ
diff --git a/bridge/etc/bridge.urm.puml b/bridge/etc/bridge.urm.puml
new file mode 100644
index 000000000000..d5d6a38a91d9
--- /dev/null
+++ b/bridge/etc/bridge.urm.puml
@@ -0,0 +1,58 @@
+@startuml
+package com.iluwatar.bridge {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ + main(args : String[]) {static}
+ }
+ interface Enchantment {
+ + apply() {abstract}
+ + onActivate() {abstract}
+ + onDeactivate() {abstract}
+ }
+ class FlyingEnchantment {
+ - LOGGER : Logger {static}
+ + FlyingEnchantment()
+ + apply()
+ + onActivate()
+ + onDeactivate()
+ }
+ class Hammer {
+ - LOGGER : Logger {static}
+ - enchantment : Enchantment
+ + Hammer(enchantment : Enchantment)
+ + getEnchantment() : Enchantment
+ + swing()
+ + unwield()
+ + wield()
+ }
+ class SoulEatingEnchantment {
+ - LOGGER : Logger {static}
+ + SoulEatingEnchantment()
+ + apply()
+ + onActivate()
+ + onDeactivate()
+ }
+ class Sword {
+ - LOGGER : Logger {static}
+ - enchantment : Enchantment
+ + Sword(enchantment : Enchantment)
+ + getEnchantment() : Enchantment
+ + swing()
+ + unwield()
+ + wield()
+ }
+ interface Weapon {
+ + getEnchantment() : Enchantment {abstract}
+ + swing() {abstract}
+ + unwield() {abstract}
+ + wield() {abstract}
+ }
+}
+Sword --> "-enchantment" Enchantment
+Hammer --> "-enchantment" Enchantment
+FlyingEnchantment ..|> Enchantment
+Hammer ..|> Weapon
+SoulEatingEnchantment ..|> Enchantment
+Sword ..|> Weapon
+@enduml
\ No newline at end of file
diff --git a/bridge/etc/bridge_1.png b/bridge/etc/bridge_1.png
deleted file mode 100644
index 3152efdf6651..000000000000
Binary files a/bridge/etc/bridge_1.png and /dev/null differ
diff --git a/bridge/pom.xml b/bridge/pom.xml
index d633718dbe23..3cfd33997c05 100644
--- a/bridge/pom.xml
+++ b/bridge/pom.xml
@@ -1,18 +1,75 @@
-
-
+
+
+
4.0.0
com.iluwatar
java-design-patterns
- 1.4.0
+ 1.26.0-SNAPSHOT
bridge
- junit
- junit
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.mockito
+ mockito-core
test
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.bridge.App
+
+
+
+
+
+
+
+
diff --git a/bridge/src/main/java/com/iluwatar/bridge/App.java b/bridge/src/main/java/com/iluwatar/bridge/App.java
index 3797653d458c..c3ea3d50c35a 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/App.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/App.java
@@ -1,35 +1,63 @@
-package com.iluwatar.bridge;
-
-/**
- *
- * In Bridge pattern both abstraction (MagicWeapon) and implementation
- * (MagicWeaponImp) have their own class hierarchies. The interface of the
- * implementations can be changed without affecting the clients.
- *
- */
-public class App {
-
- public static void main(String[] args) {
- BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(
- new Excalibur());
- blindingMagicWeapon.wield();
- blindingMagicWeapon.blind();
- blindingMagicWeapon.swing();
- blindingMagicWeapon.unwield();
-
- FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(
- new Mjollnir());
- flyingMagicWeapon.wield();
- flyingMagicWeapon.fly();
- flyingMagicWeapon.swing();
- flyingMagicWeapon.unwield();
-
- SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(
- new Stormbringer());
- soulEatingMagicWeapon.wield();
- soulEatingMagicWeapon.swing();
- soulEatingMagicWeapon.eatSoul();
- soulEatingMagicWeapon.unwield();
-
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Composition over inheritance. The Bridge pattern can also be thought of as two layers of
+ * abstraction. With Bridge, you can decouple an abstraction from its implementation so that the two
+ * can vary independently.
+ *
+ * In Bridge pattern both abstraction ({@link Weapon}) and implementation ( {@link Enchantment})
+ * have their own class hierarchies. The interface of the implementations can be changed without
+ * affecting the clients.
+ *
+ *
In this example we have two class hierarchies. One of weapons and another one of enchantments.
+ * We can easily combine any weapon with any enchantment using composition instead of creating deep
+ * class hierarchy.
+ */
+@Slf4j
+public class App {
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+ LOGGER.info("The knight receives an enchanted sword.");
+ var enchantedSword = new Sword(new SoulEatingEnchantment());
+ enchantedSword.wield();
+ enchantedSword.swing();
+ enchantedSword.unwield();
+
+ LOGGER.info("The valkyrie receives an enchanted hammer.");
+ var hammer = new Hammer(new FlyingEnchantment());
+ hammer.wield();
+ hammer.swing();
+ hammer.unwield();
+ }
+}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java
deleted file mode 100644
index 5421c6d5ef5c..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.iluwatar.bridge;
-
-public class BlindingMagicWeapon extends MagicWeapon {
-
- public BlindingMagicWeapon(BlindingMagicWeaponImp imp) {
- super(imp);
- }
-
- @Override
- public BlindingMagicWeaponImp getImp() {
- return (BlindingMagicWeaponImp) imp;
- }
-
- @Override
- public void wield() {
- getImp().wieldImp();
- }
-
- @Override
- public void swing() {
- getImp().swingImp();
- }
-
- @Override
- public void unwield() {
- getImp().unwieldImp();
- }
-
- public void blind() {
- getImp().blindImp();
- }
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImp.java b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImp.java
deleted file mode 100644
index 0686ce9177b4..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImp.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.iluwatar.bridge;
-
-public abstract class BlindingMagicWeaponImp extends MagicWeaponImp {
-
- public abstract void blindImp();
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java b/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java
new file mode 100644
index 000000000000..4bdd4502fd47
--- /dev/null
+++ b/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java
@@ -0,0 +1,35 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+/** Enchantment. */
+public interface Enchantment {
+
+ void onActivate();
+
+ void apply();
+
+ void onDeactivate();
+}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java b/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java
deleted file mode 100644
index 52298c900ac1..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.iluwatar.bridge;
-
-public class Excalibur extends BlindingMagicWeaponImp {
-
- @Override
- public void wieldImp() {
- System.out.println("wielding Excalibur");
- }
-
- @Override
- public void swingImp() {
- System.out.println("swinging Excalibur");
- }
-
- @Override
- public void unwieldImp() {
- System.out.println("unwielding Excalibur");
- }
-
- @Override
- public void blindImp() {
- System.out
- .println("bright light streams from Excalibur blinding the enemy");
- }
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java
new file mode 100644
index 000000000000..42da3523fb31
--- /dev/null
+++ b/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java
@@ -0,0 +1,47 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+import lombok.extern.slf4j.Slf4j;
+
+/** FlyingEnchantment. */
+@Slf4j
+public class FlyingEnchantment implements Enchantment {
+
+ @Override
+ public void onActivate() {
+ LOGGER.info("The item begins to glow faintly.");
+ }
+
+ @Override
+ public void apply() {
+ LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
+ }
+
+ @Override
+ public void onDeactivate() {
+ LOGGER.info("The item's glow fades.");
+ }
+}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java
deleted file mode 100644
index 9cb1902f9f27..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.iluwatar.bridge;
-
-public class FlyingMagicWeapon extends MagicWeapon {
-
- public FlyingMagicWeapon(FlyingMagicWeaponImp imp) {
- super(imp);
- }
-
- public FlyingMagicWeaponImp getImp() {
- return (FlyingMagicWeaponImp) imp;
- }
-
- @Override
- public void wield() {
- getImp().wieldImp();
- }
-
- @Override
- public void swing() {
- getImp().swingImp();
- }
-
- @Override
- public void unwield() {
- getImp().unwieldImp();
- }
-
- public void fly() {
- getImp().flyImp();
- }
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImp.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImp.java
deleted file mode 100644
index 37fa8a0f9fa3..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImp.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.iluwatar.bridge;
-
-public abstract class FlyingMagicWeaponImp extends MagicWeaponImp {
-
- public abstract void flyImp();
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Hammer.java b/bridge/src/main/java/com/iluwatar/bridge/Hammer.java
new file mode 100644
index 000000000000..328f3b79e9d6
--- /dev/null
+++ b/bridge/src/main/java/com/iluwatar/bridge/Hammer.java
@@ -0,0 +1,59 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/** Hammer. */
+@Slf4j
+@AllArgsConstructor
+public class Hammer implements Weapon {
+
+ private final Enchantment enchantment;
+
+ @Override
+ public void wield() {
+ LOGGER.info("The hammer is wielded.");
+ enchantment.onActivate();
+ }
+
+ @Override
+ public void swing() {
+ LOGGER.info("The hammer is swung.");
+ enchantment.apply();
+ }
+
+ @Override
+ public void unwield() {
+ LOGGER.info("The hammer is unwielded.");
+ enchantment.onDeactivate();
+ }
+
+ @Override
+ public Enchantment getEnchantment() {
+ return enchantment;
+ }
+}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java
deleted file mode 100644
index b03435c518e9..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.iluwatar.bridge;
-
-/**
- *
- * Abstraction interface.
- *
- */
-public abstract class MagicWeapon {
-
- protected MagicWeaponImp imp;
-
- public MagicWeapon(MagicWeaponImp imp) {
- this.imp = imp;
- }
-
- public abstract void wield();
-
- public abstract void swing();
-
- public abstract void unwield();
-
- public MagicWeaponImp getImp() {
- return imp;
- }
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImp.java b/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImp.java
deleted file mode 100644
index cf972c115451..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImp.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.iluwatar.bridge;
-
-/**
- *
- * Implementation interface.
- *
- */
-public abstract class MagicWeaponImp {
-
- public abstract void wieldImp();
-
- public abstract void swingImp();
-
- public abstract void unwieldImp();
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java b/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java
deleted file mode 100644
index 0c21f8d077ec..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.iluwatar.bridge;
-
-public class Mjollnir extends FlyingMagicWeaponImp {
-
- @Override
- public void wieldImp() {
- System.out.println("wielding Mjollnir");
- }
-
- @Override
- public void swingImp() {
- System.out.println("swinging Mjollnir");
- }
-
- @Override
- public void unwieldImp() {
- System.out.println("unwielding Mjollnir");
- }
-
- @Override
- public void flyImp() {
- System.out
- .println("Mjollnir hits the enemy in the air and returns back to the owner's hand");
- }
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java
new file mode 100644
index 000000000000..ed22174673d3
--- /dev/null
+++ b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java
@@ -0,0 +1,47 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+import lombok.extern.slf4j.Slf4j;
+
+/** SoulEatingEnchantment. */
+@Slf4j
+public class SoulEatingEnchantment implements Enchantment {
+
+ @Override
+ public void onActivate() {
+ LOGGER.info("The item spreads bloodlust.");
+ }
+
+ @Override
+ public void apply() {
+ LOGGER.info("The item eats the soul of enemies.");
+ }
+
+ @Override
+ public void onDeactivate() {
+ LOGGER.info("Bloodlust slowly disappears.");
+ }
+}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java
deleted file mode 100644
index dbbec102d94f..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.iluwatar.bridge;
-
-public class SoulEatingMagicWeapon extends MagicWeapon {
-
- public SoulEatingMagicWeapon(SoulEatingMagicWeaponImp imp) {
- super(imp);
- }
-
- @Override
- public SoulEatingMagicWeaponImp getImp() {
- return (SoulEatingMagicWeaponImp) imp;
- }
-
- @Override
- public void wield() {
- getImp().wieldImp();
- }
-
- @Override
- public void swing() {
- getImp().swingImp();
- }
-
- @Override
- public void unwield() {
- getImp().unwieldImp();
- }
-
- public void eatSoul() {
- getImp().eatSoulImp();
- }
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImp.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImp.java
deleted file mode 100644
index 3c1a557dd46a..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImp.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.iluwatar.bridge;
-
-public abstract class SoulEatingMagicWeaponImp extends MagicWeaponImp {
-
- public abstract void eatSoulImp();
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java b/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java
deleted file mode 100644
index 55b5960c9bf5..000000000000
--- a/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.iluwatar.bridge;
-
-public class Stormbringer extends SoulEatingMagicWeaponImp {
-
- @Override
- public void wieldImp() {
- System.out.println("wielding Stormbringer");
- }
-
- @Override
- public void swingImp() {
- System.out.println("swinging Stormbringer");
- }
-
- @Override
- public void unwieldImp() {
- System.out.println("unwielding Stormbringer");
- }
-
- @Override
- public void eatSoulImp() {
- System.out.println("Stormbringer devours the enemy's soul");
- }
-
-}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Sword.java b/bridge/src/main/java/com/iluwatar/bridge/Sword.java
new file mode 100644
index 000000000000..417bc9334046
--- /dev/null
+++ b/bridge/src/main/java/com/iluwatar/bridge/Sword.java
@@ -0,0 +1,59 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/** Sword. */
+@Slf4j
+@AllArgsConstructor
+public class Sword implements Weapon {
+
+ private final Enchantment enchantment;
+
+ @Override
+ public void wield() {
+ LOGGER.info("The sword is wielded.");
+ enchantment.onActivate();
+ }
+
+ @Override
+ public void swing() {
+ LOGGER.info("The sword is swung.");
+ enchantment.apply();
+ }
+
+ @Override
+ public void unwield() {
+ LOGGER.info("The sword is unwielded.");
+ enchantment.onDeactivate();
+ }
+
+ @Override
+ public Enchantment getEnchantment() {
+ return enchantment;
+ }
+}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Weapon.java b/bridge/src/main/java/com/iluwatar/bridge/Weapon.java
new file mode 100644
index 000000000000..a9f0ab4bbf52
--- /dev/null
+++ b/bridge/src/main/java/com/iluwatar/bridge/Weapon.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+/** Weapon. */
+public interface Weapon {
+
+ void wield();
+
+ void swing();
+
+ void unwield();
+
+ Enchantment getEnchantment();
+}
diff --git a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java
index 196008950a98..d1136fc90f41 100644
--- a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java
+++ b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java
@@ -1,14 +1,44 @@
-package com.iluwatar.bridge;
-
-import org.junit.Test;
-
-import com.iluwatar.bridge.App;
-
-public class AppTest {
-
- @Test
- public void test() {
- String[] args = {};
- App.main(args);
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Application 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} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java b/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java
new file mode 100644
index 000000000000..d8853647cb84
--- /dev/null
+++ b/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java
@@ -0,0 +1,44 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests for hammer */
+class HammerTest extends WeaponTest {
+
+ /**
+ * Invoke all possible actions on the weapon and check if the actions are executed on the actual
+ * underlying weapon implementation.
+ */
+ @Test
+ void testHammer() {
+ final var hammer = spy(new Hammer(mock(FlyingEnchantment.class)));
+ testBasicWeaponActions(hammer);
+ }
+}
diff --git a/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java b/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java
new file mode 100644
index 000000000000..b021cd08d00c
--- /dev/null
+++ b/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java
@@ -0,0 +1,44 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests for sword */
+class SwordTest extends WeaponTest {
+
+ /**
+ * Invoke all possible actions on the weapon and check if the actions are executed on the actual
+ * underlying weapon implementation.
+ */
+ @Test
+ void testSword() {
+ final var sword = spy(new Sword(mock(FlyingEnchantment.class)));
+ testBasicWeaponActions(sword);
+ }
+}
diff --git a/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java
new file mode 100644
index 000000000000..67648ea6391e
--- /dev/null
+++ b/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java
@@ -0,0 +1,56 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bridge;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+/** Base class for weapon tests */
+abstract class WeaponTest {
+
+ /**
+ * Invoke the basic actions of the given weapon, and test if the underlying enchantment
+ * implementation is invoked
+ */
+ final void testBasicWeaponActions(final Weapon weapon) {
+ assertNotNull(weapon);
+ var enchantment = weapon.getEnchantment();
+ assertNotNull(enchantment);
+ assertNotNull(weapon.getEnchantment());
+
+ weapon.swing();
+ verify(enchantment).apply();
+ verifyNoMoreInteractions(enchantment);
+
+ weapon.wield();
+ verify(enchantment).onActivate();
+ verifyNoMoreInteractions(enchantment);
+
+ weapon.unwield();
+ verify(enchantment).onDeactivate();
+ verifyNoMoreInteractions(enchantment);
+ }
+}
diff --git a/builder/README.md b/builder/README.md
new file mode 100644
index 000000000000..cf6e3242dd51
--- /dev/null
+++ b/builder/README.md
@@ -0,0 +1,204 @@
+---
+title: "Builder Pattern in Java: Crafting Custom Objects with Clarity"
+shortTitle: Builder
+description: "Discover the Builder design pattern in Java, a powerful creational pattern that simplifies object construction. Learn how to separate the construction of a complex object from its representation with practical examples and use cases."
+category: Creational
+language: en
+tag:
+ - Gang of Four
+ - Instantiation
+ - Object composition
+---
+
+## Intent of Builder Design Pattern
+
+The Builder design pattern in Java, a fundamental creational pattern, allows for the step-by-step construction of complex objects. It separates the construction of a complex object from its representation so that the same construction process can create different representations.
+
+## Detailed Explanation of Builder Pattern with Real-World Examples
+
+Real-world example
+
+> The Java Builder pattern is particularly useful in scenarios where object creation involves numerous parameters.
+>
+> Imagine you are building a customizable sandwich at a deli. The Builder design pattern in this context would involve a SandwichBuilder that allows you to specify each component of the sandwich, such as the type of bread, meat, cheese, vegetables, and condiments. Instead of having to know how to construct the sandwich from scratch, you use the SandwichBuilder to add each desired component step-by-step, ensuring you get exactly the sandwich you want. This separation of construction from the final product representation ensures that the same construction process can yield different types of sandwiches based on the specified components.
+
+In plain words
+
+> Allows you to create different flavors of an object while avoiding constructor pollution. Useful when there could be several flavors of an object. Or when there are a lot of steps involved in creation of an object.
+
+Wikipedia says
+
+> The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor antipattern.
+
+With that in mind, let's explain what the telescoping constructor antipattern is. At some point, we have all encountered a constructor like the one below:
+
+```java
+public Hero(Profession profession,String name,HairType hairType,HairColor hairColor,Armor armor,Weapon weapon){
+ // Value assignments
+}
+```
+
+As you can see, the number of constructor parameters can quickly become overwhelming, making it difficult to understand their arrangement. Additionally, this list of parameters might continue to grow if you decide to add more options in the future. This is known as the telescoping constructor antipattern.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Builder Pattern in Java
+
+In this Java Builder pattern example, we construct different types of `Hero` objects with varying attributes.
+
+Imagine a character generator for a role-playing game. The simplest option is to let the computer generate the character for you. However, if you prefer to manually select character details such as profession, gender, hair color, etc., the character creation becomes a step-by-step process that concludes once all selections are made.
+
+A more sensible approach is to use the Builder pattern. First, let's consider the `Hero` that we want to create:
+
+```java
+public final class Hero {
+ private final Profession profession;
+ private final String name;
+ private final HairType hairType;
+ private final HairColor hairColor;
+ private final Armor armor;
+ private final Weapon weapon;
+
+ private Hero(Builder builder) {
+ this.profession = builder.profession;
+ this.name = builder.name;
+ this.hairColor = builder.hairColor;
+ this.hairType = builder.hairType;
+ this.weapon = builder.weapon;
+ this.armor = builder.armor;
+ }
+}
+```
+
+Then we have the `Builder`:
+
+```java
+ public static class Builder {
+ private final Profession profession;
+ private final String name;
+ private HairType hairType;
+ private HairColor hairColor;
+ private Armor armor;
+ private Weapon weapon;
+
+ public Builder(Profession profession, String name) {
+ if (profession == null || name == null) {
+ throw new IllegalArgumentException("profession and name can not be null");
+ }
+ this.profession = profession;
+ this.name = name;
+ }
+
+ public Builder withHairType(HairType hairType) {
+ this.hairType = hairType;
+ return this;
+ }
+
+ public Builder withHairColor(HairColor hairColor) {
+ this.hairColor = hairColor;
+ return this;
+ }
+
+ public Builder withArmor(Armor armor) {
+ this.armor = armor;
+ return this;
+ }
+
+ public Builder withWeapon(Weapon weapon) {
+ this.weapon = weapon;
+ return this;
+ }
+
+ public Hero build() {
+ return new Hero(this);
+ }
+}
+```
+
+Then it can be used as:
+
+```java
+ public static void main(String[] args) {
+
+ var mage = new Hero.Builder(Profession.MAGE, "Riobard")
+ .withHairColor(HairColor.BLACK)
+ .withWeapon(Weapon.DAGGER)
+ .build();
+ LOGGER.info(mage.toString());
+
+ var warrior = new Hero.Builder(Profession.WARRIOR, "Amberjill")
+ .withHairColor(HairColor.BLOND)
+ .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD)
+ .build();
+ LOGGER.info(warrior.toString());
+
+ var thief = new Hero.Builder(Profession.THIEF, "Desmond")
+ .withHairType(HairType.BALD)
+ .withWeapon(Weapon.BOW)
+ .build();
+ LOGGER.info(thief.toString());
+}
+```
+
+Program output:
+
+```
+16:28:06.058 [main] INFO com.iluwatar.builder.App -- This is a mage named Riobard with black hair and wielding a dagger.
+16:28:06.060 [main] INFO com.iluwatar.builder.App -- This is a warrior named Amberjill with blond long curly hair wearing chain mail and wielding a sword.
+16:28:06.060 [main] INFO com.iluwatar.builder.App -- This is a thief named Desmond with bald head and wielding a bow.
+```
+
+## When to Use the Builder Pattern in Java
+
+Use the Builder pattern when
+
+* The Builder pattern is ideal for Java applications requiring complex object creation.
+* The algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled
+* The construction process must allow different representations for the object that's constructed
+* It's particularly useful when a product requires a lot of steps to be created and when these steps need to be executed in a specific sequence
+
+## Builder Pattern Java Tutorials
+
+* [Builder Design Pattern in Java (DigitalOcean)](https://www.journaldev.com/1425/builder-design-pattern-in-java)
+* [Builder (Refactoring Guru)](https://refactoring.guru/design-patterns/builder)
+* [Exploring Joshua Bloch’s Builder design pattern in Java (Java Magazine)](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java)
+
+## Real-World Applications of Builder Pattern in Java
+
+* StringBuilder in Java for constructing strings.
+* java.lang.StringBuffer used to create mutable string objects.
+* Java.nio.ByteBuffer as well as similar buffers such as FloatBuffer, IntBuffer, and others
+* javax.swing.GroupLayout.Group#addComponent()
+* Various GUI builders in IDEs that construct UI components.
+* All implementations of [java.lang.Appendable](http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html)
+* [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
+* [Apache Commons Option.Builder](https://commons.apache.org/proper/commons-cli/apidocs/org/apache/commons/cli/Option.Builder.html)
+
+## Benefits and Trade-offs of Builder Pattern
+
+Benefits:
+
+* More control over the construction process compared to other creational patterns
+* Supports constructing objects step-by-step, defer construction steps or run steps recursively
+* Can construct objects that require a complex assembly of sub-objects. The final product is detached from the parts that make it up, as well as their assembly process
+* Single Responsibility Principle. You can isolate complex construction code from the business logic of the product
+
+Trade-offs:
+
+* The overall complexity of the code can increase since the pattern requires creating multiple new classes
+* May increase memory usage due to the necessity of creating multiple builder objects
+
+## Related Java Design Patterns
+
+* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): Can be used in conjunction with Builder to build parts of a complex object.
+* [Prototype](https://java-design-patterns.com/patterns/prototype/): Builders often create objects from a prototype.
+* [Step Builder](https://java-design-patterns.com/patterns/step-builder/): It is a variation of the Builder pattern that generates a complex object using a step-by-step approach. The Step Builder pattern is a good choice when you need to build an object with a large number of optional parameters, and you want to avoid the telescoping constructor antipattern.
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
diff --git a/builder/etc/builder-sequence-diagram.png b/builder/etc/builder-sequence-diagram.png
new file mode 100644
index 000000000000..355556637870
Binary files /dev/null and b/builder/etc/builder-sequence-diagram.png differ
diff --git a/builder/etc/builder.png b/builder/etc/builder.png
deleted file mode 100644
index a0280ba53f7d..000000000000
Binary files a/builder/etc/builder.png and /dev/null differ
diff --git a/builder/etc/builder.ucls b/builder/etc/builder.ucls
deleted file mode 100644
index 06a83ced7f79..000000000000
--- a/builder/etc/builder.ucls
+++ /dev/null
@@ -1,162 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/builder/etc/builder.urm.png b/builder/etc/builder.urm.png
new file mode 100644
index 000000000000..d77808d36097
Binary files /dev/null and b/builder/etc/builder.urm.png differ
diff --git a/builder/etc/builder.urm.puml b/builder/etc/builder.urm.puml
new file mode 100644
index 000000000000..43d595a176fc
--- /dev/null
+++ b/builder/etc/builder.urm.puml
@@ -0,0 +1,100 @@
+@startuml
+package com.iluwatar.builder {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ + main(args : String[]) {static}
+ }
+ enum Armor {
+ + CHAIN_MAIL {static}
+ + CLOTHES {static}
+ + LEATHER {static}
+ + PLATE_MAIL {static}
+ - title : String
+ + toString() : String
+ + valueOf(name : String) : Armor {static}
+ + values() : Armor[] {static}
+ }
+ enum HairColor {
+ + BLACK {static}
+ + BLOND {static}
+ + BROWN {static}
+ + RED {static}
+ + WHITE {static}
+ + toString() : String
+ + valueOf(name : String) : HairColor {static}
+ + values() : HairColor[] {static}
+ }
+ enum HairType {
+ + BALD {static}
+ + CURLY {static}
+ + LONG_CURLY {static}
+ + LONG_STRAIGHT {static}
+ + SHORT {static}
+ - title : String
+ + toString() : String
+ + valueOf(name : String) : HairType {static}
+ + values() : HairType[] {static}
+ }
+ class Hero {
+ - armor : Armor
+ - hairColor : HairColor
+ - hairType : HairType
+ - name : String
+ - profession : Profession
+ - weapon : Weapon
+ - Hero(builder : Builder)
+ + getArmor() : Armor
+ + getHairColor() : HairColor
+ + getHairType() : HairType
+ + getName() : String
+ + getProfession() : Profession
+ + getWeapon() : Weapon
+ + toString() : String
+ }
+ class Builder {
+ - armor : Armor
+ - hairColor : HairColor
+ - hairType : HairType
+ - name : String
+ - profession : Profession
+ - weapon : Weapon
+ + Builder(profession : Profession, name : String)
+ + build() : Hero
+ + withArmor(armor : Armor) : Builder
+ + withHairColor(hairColor : HairColor) : Builder
+ + withHairType(hairType : HairType) : Builder
+ + withWeapon(weapon : Weapon) : Builder
+ }
+ enum Profession {
+ + MAGE {static}
+ + PRIEST {static}
+ + THIEF {static}
+ + WARRIOR {static}
+ + toString() : String
+ + valueOf(name : String) : Profession {static}
+ + values() : Profession[] {static}
+ }
+ enum Weapon {
+ + AXE {static}
+ + BOW {static}
+ + DAGGER {static}
+ + SWORD {static}
+ + WARHAMMER {static}
+ + toString() : String
+ + valueOf(name : String) : Weapon {static}
+ + values() : Weapon[] {static}
+ }
+}
+Hero --> "-profession" Profession
+Builder ..+ Hero
+Hero --> "-armor" Armor
+Builder --> "-hairColor" HairColor
+Builder --> "-weapon" Weapon
+Builder --> "-hairType" HairType
+Hero --> "-hairColor" HairColor
+Builder --> "-profession" Profession
+Hero --> "-weapon" Weapon
+Hero --> "-hairType" HairType
+Builder --> "-armor" Armor
+@enduml
\ No newline at end of file
diff --git a/builder/etc/builder_1.png b/builder/etc/builder_1.png
deleted file mode 100644
index ec892c15f6c4..000000000000
Binary files a/builder/etc/builder_1.png and /dev/null differ
diff --git a/builder/pom.xml b/builder/pom.xml
index 9649f84544eb..3677c187d5e6 100644
--- a/builder/pom.xml
+++ b/builder/pom.xml
@@ -1,18 +1,70 @@
-
-
+
+
+
4.0.0
com.iluwatar
java-design-patterns
- 1.4.0
+ 1.26.0-SNAPSHOT
builder
- junit
- junit
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
test
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.builder.App
+
+
+
+
+
+
+
+
diff --git a/builder/src/main/java/com/iluwatar/builder/App.java b/builder/src/main/java/com/iluwatar/builder/App.java
index e38e5a0c3ea5..acec73a48ee1 100644
--- a/builder/src/main/java/com/iluwatar/builder/App.java
+++ b/builder/src/main/java/com/iluwatar/builder/App.java
@@ -1,38 +1,84 @@
-package com.iluwatar.builder;
-
-import com.iluwatar. builder.Hero.HeroBuilder;
-
-/**
- *
- * This is the Builder pattern variation as described by Joshua Bloch in
- * Effective Java 2nd Edition.
- *
- * We want to build Hero objects, but its construction is complex because of the
- * many parameters needed. To aid the user we introduce HeroBuilder class.
- * HeroBuilder takes the minimum parameters to build Hero object in its
- * constructor. After that additional configuration for the Hero object can be
- * done using the fluent HeroBuilder interface. When configuration is ready the
- * build method is called to receive the final Hero object.
- *
- */
-public class App {
-
- public static void main(String[] args) {
-
- Hero mage = new HeroBuilder(Profession.MAGE, "Riobard")
- .withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER)
- .build();
- System.out.println(mage);
-
- Hero warrior = new HeroBuilder(Profession.WARRIOR, "Amberjill")
- .withHairColor(HairColor.BLOND)
- .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL)
- .withWeapon(Weapon.SWORD).build();
- System.out.println(warrior);
-
- Hero thief = new HeroBuilder(Profession.THIEF, "Desmond")
- .withHairType(HairType.BALD).withWeapon(Weapon.BOW).build();
- System.out.println(thief);
-
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.builder;
+
+import com.iluwatar.builder.Hero.Builder;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * The intention of the Builder pattern is to find a solution to the telescoping constructor
+ * antipattern. The telescoping constructor antipattern occurs when the increase of object
+ * constructor parameter combination leads to an exponential list of constructors. Instead of using
+ * numerous constructors, the builder pattern uses another object, a builder, that receives each
+ * initialization parameter step by step and then returns the resulting constructed object at once.
+ *
+ * The Builder pattern has another benefit. It can be used for objects that contain flat data
+ * (html code, SQL query, X.509 certificate...), that is to say, data that can't be easily edited.
+ * This type of data cannot be edited step by step and must be edited at once. The best way to
+ * construct such an object is to use a builder class.
+ *
+ *
In this example we have the Builder pattern variation as described by Joshua Bloch in
+ * Effective Java 2nd Edition.
+ *
+ *
We want to build {@link Hero} objects, but its construction is complex because of the many
+ * parameters needed. To aid the user we introduce {@link Builder} class. {@link Hero.Builder} takes
+ * the minimum parameters to build {@link Hero} object in its constructor. After that additional
+ * configuration for the {@link Hero} object can be done using the fluent {@link Builder} interface.
+ * When configuration is ready the build method is called to receive the final {@link Hero} object.
+ */
+@Slf4j
+public class App {
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+
+ var mage =
+ new Hero.Builder(Profession.MAGE, "Riobard")
+ .withHairColor(HairColor.BLACK)
+ .withWeapon(Weapon.DAGGER)
+ .build();
+ LOGGER.info(mage.toString());
+
+ var warrior =
+ new Hero.Builder(Profession.WARRIOR, "Amberjill")
+ .withHairColor(HairColor.BLOND)
+ .withHairType(HairType.LONG_CURLY)
+ .withArmor(Armor.CHAIN_MAIL)
+ .withWeapon(Weapon.SWORD)
+ .build();
+ LOGGER.info(warrior.toString());
+
+ var thief =
+ new Hero.Builder(Profession.THIEF, "Desmond")
+ .withHairType(HairType.BALD)
+ .withWeapon(Weapon.BOW)
+ .build();
+ LOGGER.info(thief.toString());
+ }
+}
diff --git a/builder/src/main/java/com/iluwatar/builder/Armor.java b/builder/src/main/java/com/iluwatar/builder/Armor.java
index ebec6a47dbb6..1710f569af5e 100644
--- a/builder/src/main/java/com/iluwatar/builder/Armor.java
+++ b/builder/src/main/java/com/iluwatar/builder/Armor.java
@@ -1,17 +1,43 @@
-package com.iluwatar.builder;
-
-public enum Armor {
-
- CLOTHES("clothes"), LEATHER("leather"), CHAIN_MAIL("chain mail"), PLATE_MAIL("plate mail");
-
- private String title;
-
- Armor(String title) {
- this.title = title;
- }
-
- @Override
- public String toString() {
- return title;
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.builder;
+
+import lombok.AllArgsConstructor;
+
+/** Armor enumeration. */
+@AllArgsConstructor
+public enum Armor {
+ CLOTHES("clothes"),
+ LEATHER("leather"),
+ CHAIN_MAIL("chain mail"),
+ PLATE_MAIL("plate mail");
+
+ private final String title;
+
+ @Override
+ public String toString() {
+ return title;
+ }
+}
diff --git a/builder/src/main/java/com/iluwatar/builder/HairColor.java b/builder/src/main/java/com/iluwatar/builder/HairColor.java
index a73044754e0d..7f767c98d661 100644
--- a/builder/src/main/java/com/iluwatar/builder/HairColor.java
+++ b/builder/src/main/java/com/iluwatar/builder/HairColor.java
@@ -1,12 +1,39 @@
-package com.iluwatar.builder;
-
-public enum HairColor {
-
- WHITE, BLOND, RED, BROWN, BLACK;
-
- @Override
- public String toString() {
- return name().toLowerCase();
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.builder;
+
+/** HairColor enumeration. */
+public enum HairColor {
+ WHITE,
+ BLOND,
+ RED,
+ BROWN,
+ BLACK;
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+}
diff --git a/builder/src/main/java/com/iluwatar/builder/HairType.java b/builder/src/main/java/com/iluwatar/builder/HairType.java
index 075699a582bf..7ac31d0fa03e 100644
--- a/builder/src/main/java/com/iluwatar/builder/HairType.java
+++ b/builder/src/main/java/com/iluwatar/builder/HairType.java
@@ -1,17 +1,44 @@
-package com.iluwatar.builder;
-
-public enum HairType {
-
- BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY("long curly");
-
- private String title;
-
- HairType(String title) {
- this.title = title;
- }
-
- @Override
- public String toString() {
- return title;
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.builder;
+
+import lombok.AllArgsConstructor;
+
+/** HairType enumeration. */
+@AllArgsConstructor
+public enum HairType {
+ BALD("bald"),
+ SHORT("short"),
+ CURLY("curly"),
+ LONG_STRAIGHT("long straight"),
+ LONG_CURLY("long curly");
+
+ private final String title;
+
+ @Override
+ public String toString() {
+ return title;
+ }
+}
diff --git a/builder/src/main/java/com/iluwatar/builder/Hero.java b/builder/src/main/java/com/iluwatar/builder/Hero.java
index 75513eedad27..a87137e51fe0 100644
--- a/builder/src/main/java/com/iluwatar/builder/Hero.java
+++ b/builder/src/main/java/com/iluwatar/builder/Hero.java
@@ -1,129 +1,114 @@
-package com.iluwatar.builder;
-
-/**
- *
- * The class with many parameters.
- *
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
*/
-public class Hero {
-
- private final Profession profession;
- private final String name;
- private final HairType hairType;
- private final HairColor hairColor;
- private final Armor armor;
- private final Weapon weapon;
-
- public Profession getProfession() {
- return profession;
- }
-
- public String getName() {
- return name;
- }
-
- public HairType getHairType() {
- return hairType;
- }
-
- public HairColor getHairColor() {
- return hairColor;
- }
-
- public Armor getArmor() {
- return armor;
- }
-
- public Weapon getWeapon() {
- return weapon;
- }
-
- @Override
- public String toString() {
-
- StringBuilder sb = new StringBuilder();
- sb.append("This is a ");
- sb.append(profession);
- sb.append(" named ");
- sb.append(name);
- if (hairColor != null || hairType != null) {
- sb.append(" with ");
- if (hairColor != null) {
- sb.append(hairColor);
- sb.append(" ");
- }
- if (hairType != null) {
- sb.append(hairType);
- sb.append(" ");
- }
- sb.append(hairType != HairType.BALD ? "hair" : "head");
- }
- if (armor != null) {
- sb.append(" wearing ");
- sb.append(armor);
- }
- if (weapon != null) {
- sb.append(" and wielding a ");
- sb.append(weapon);
- }
- sb.append(".");
- return sb.toString();
- }
-
- private Hero(HeroBuilder builder) {
- this.profession = builder.profession;
- this.name = builder.name;
- this.hairColor = builder.hairColor;
- this.hairType = builder.hairType;
- this.weapon = builder.weapon;
- this.armor = builder.armor;
- }
-
- /**
- *
- * The builder class.
- *
- */
- public static class HeroBuilder {
-
- private final Profession profession;
- private final String name;
- private HairType hairType;
- private HairColor hairColor;
- private Armor armor;
- private Weapon weapon;
-
- public HeroBuilder(Profession profession, String name) {
- if (profession == null || name == null) {
- throw new IllegalArgumentException(
- "profession and name can not be null");
- }
- this.profession = profession;
- this.name = name;
- }
-
- public HeroBuilder withHairType(HairType hairType) {
- this.hairType = hairType;
- return this;
- }
-
- public HeroBuilder withHairColor(HairColor hairColor) {
- this.hairColor = hairColor;
- return this;
- }
-
- public HeroBuilder withArmor(Armor armor) {
- this.armor = armor;
- return this;
- }
-
- public HeroBuilder withWeapon(Weapon weapon) {
- this.weapon = weapon;
- return this;
- }
+package com.iluwatar.builder;
- public Hero build() {
- return new Hero(this);
- }
- }
+/** Hero,the record class. */
+public record Hero(
+ Profession profession,
+ String name,
+ HairType hairType,
+ HairColor hairColor,
+ Armor armor,
+ Weapon weapon) {
+
+ private Hero(Builder builder) {
+ this(
+ builder.profession,
+ builder.name,
+ builder.hairType,
+ builder.hairColor,
+ builder.armor,
+ builder.weapon);
+ }
+
+ @Override
+ public String toString() {
+
+ var sb = new StringBuilder();
+ sb.append("This is a ").append(profession).append(" named ").append(name);
+ if (hairColor != null || hairType != null) {
+ sb.append(" with ");
+ if (hairColor != null) {
+ sb.append(hairColor).append(' ');
+ }
+ if (hairType != null) {
+ sb.append(hairType).append(' ');
+ }
+ sb.append(hairType != HairType.BALD ? "hair" : "head");
+ }
+ if (armor != null) {
+ sb.append(" wearing ").append(armor);
+ }
+ if (weapon != null) {
+ sb.append(" and wielding a ").append(weapon);
+ }
+ sb.append('.');
+ return sb.toString();
+ }
+
+ /** The builder class. */
+ public static class Builder {
+
+ private final Profession profession;
+ private final String name;
+ private HairType hairType;
+ private HairColor hairColor;
+ private Armor armor;
+ private Weapon weapon;
+
+ /** Constructor. */
+ public Builder(Profession profession, String name) {
+ if (profession == null || name == null) {
+ throw new IllegalArgumentException("profession and name can not be null");
+ }
+ this.profession = profession;
+ this.name = name;
+ }
+
+ public Builder withHairType(HairType hairType) {
+ this.hairType = hairType;
+ return this;
+ }
+
+ public Builder withHairColor(HairColor hairColor) {
+ this.hairColor = hairColor;
+ return this;
+ }
+
+ public Builder withArmor(Armor armor) {
+ this.armor = armor;
+ return this;
+ }
+
+ public Builder withWeapon(Weapon weapon) {
+ this.weapon = weapon;
+ return this;
+ }
+
+ public Hero build() {
+ return new Hero(this);
+ }
+ }
}
diff --git a/builder/src/main/java/com/iluwatar/builder/Profession.java b/builder/src/main/java/com/iluwatar/builder/Profession.java
index 487fc5133b54..c1be949840f8 100644
--- a/builder/src/main/java/com/iluwatar/builder/Profession.java
+++ b/builder/src/main/java/com/iluwatar/builder/Profession.java
@@ -1,12 +1,38 @@
-package com.iluwatar.builder;
-
-public enum Profession {
-
- WARRIOR, THIEF, MAGE, PRIEST;
-
- @Override
- public String toString() {
- return name().toLowerCase();
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.builder;
+
+/** Profession enumeration. */
+public enum Profession {
+ WARRIOR,
+ THIEF,
+ MAGE,
+ PRIEST;
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+}
diff --git a/builder/src/main/java/com/iluwatar/builder/Weapon.java b/builder/src/main/java/com/iluwatar/builder/Weapon.java
index 99622ddacd5a..03a9565d0cbe 100644
--- a/builder/src/main/java/com/iluwatar/builder/Weapon.java
+++ b/builder/src/main/java/com/iluwatar/builder/Weapon.java
@@ -1,12 +1,39 @@
-package com.iluwatar.builder;
-
-public enum Weapon {
-
- DAGGER, SWORD, AXE, WARHAMMER, BOW;
-
- @Override
- public String toString() {
- return name().toLowerCase();
- }
-
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.builder;
+
+/** Weapon enumeration. */
+public enum Weapon {
+ DAGGER,
+ SWORD,
+ AXE,
+ WARHAMMER,
+ BOW;
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+}
diff --git a/builder/src/test/java/com/iluwatar/builder/AppTest.java b/builder/src/test/java/com/iluwatar/builder/AppTest.java
index 462ad3a9ac9b..367d3da1c1ac 100644
--- a/builder/src/test/java/com/iluwatar/builder/AppTest.java
+++ b/builder/src/test/java/com/iluwatar/builder/AppTest.java
@@ -1,14 +1,44 @@
-package com.iluwatar.builder;
-
-import org.junit.Test;
-
-import com.iluwatar. builder.App;
-
-public class AppTest {
-
- @Test
- public void test() {
- String[] args = {};
- App.main(args);
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.builder;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Application 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} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/builder/src/test/java/com/iluwatar/builder/HeroTest.java b/builder/src/test/java/com/iluwatar/builder/HeroTest.java
new file mode 100644
index 000000000000..5f67a56aa999
--- /dev/null
+++ b/builder/src/test/java/com/iluwatar/builder/HeroTest.java
@@ -0,0 +1,70 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.builder;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+/** HeroTest */
+class HeroTest {
+
+ /** Test if we get the expected exception when trying to create a hero without a profession */
+ @Test
+ void testMissingProfession() {
+ assertThrows(IllegalArgumentException.class, () -> new Hero.Builder(null, "Sir without a job"));
+ }
+
+ /** Test if we get the expected exception when trying to create a hero without a name */
+ @Test
+ void testMissingName() {
+ assertThrows(IllegalArgumentException.class, () -> new Hero.Builder(Profession.THIEF, null));
+ }
+
+ /** Test if the hero build by the builder has the correct attributes, as requested */
+ @Test
+ void testBuildHero() {
+ final String heroName = "Sir Lancelot";
+
+ final var hero =
+ new Hero.Builder(Profession.WARRIOR, heroName)
+ .withArmor(Armor.CHAIN_MAIL)
+ .withWeapon(Weapon.SWORD)
+ .withHairType(HairType.LONG_CURLY)
+ .withHairColor(HairColor.BLOND)
+ .build();
+
+ assertNotNull(hero);
+ assertNotNull(hero.toString());
+ assertEquals(Profession.WARRIOR, hero.profession());
+ assertEquals(heroName, hero.name());
+ assertEquals(Armor.CHAIN_MAIL, hero.armor());
+ assertEquals(Weapon.SWORD, hero.weapon());
+ assertEquals(HairType.LONG_CURLY, hero.hairType());
+ assertEquals(HairColor.BLOND, hero.hairColor());
+ }
+}
diff --git a/business-delegate/README.md b/business-delegate/README.md
new file mode 100644
index 000000000000..9f83e1a1748a
--- /dev/null
+++ b/business-delegate/README.md
@@ -0,0 +1,193 @@
+---
+title: "Business Delegate Pattern in Java: Simplifying Business Service Interaction"
+shortTitle: Business Delegate
+description: "Learn about the Business Delegate pattern in Java. This design pattern adds an abstraction layer between presentation and business tiers, ensuring loose coupling and easier service interaction. Includes examples and class diagrams."
+category: Structural
+language: en
+tag:
+ - Business
+ - Decoupling
+ - Delegation
+ - Enterprise patterns
+ - Layered architecture
+---
+
+## Also known as
+
+* Service Representative
+
+## Intent of Business Delegate Design Pattern
+
+The Business Delegate pattern is a structural design pattern in Java that adds an abstraction layer between the presentation and business tiers. By using the pattern we gain loose coupling between the tiers and encapsulate knowledge about how to locate, connect to, and interact with the business objects that make up the application.
+
+## Detailed Explanation of Business Delegate Pattern with Real-World Examples
+
+Real-world example
+
+> In an Enterprise application using Java EE, the Business Delegate pattern helps manage interactions between different business services.
+>
+> Imagine a restaurant where the waitstaff serves as intermediaries between the customers and the kitchen. When a customer places an order, the waiter takes the order to the kitchen, relays any specific requests, and later brings the prepared food back to the customer. The waitstaff abstracts the complexity of the kitchen operations from the customers, allowing the chefs to focus solely on cooking without needing to interact directly with customers. This setup allows both the customer service (presentation tier) and the kitchen (business service) to operate independently and efficiently. The waitstaff acts as the Business Delegate, managing communication and ensuring smooth interactions between the two distinct areas.
+
+In Plain Words
+
+> Business Delegate adds an abstraction layer between the presentation and business tiers.
+
+Wikipedia says
+
+> Business Delegate is a Java EE design pattern. This pattern is directing to reduce the coupling in between business services and the connected presentation tier, and to hide the implementation details of services (including lookup and accessibility of EJB architecture). Business Delegates acts as an adaptor to invoke business objects from the presentation tier.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Business Delegate Pattern in Java
+
+The following Java code demonstrates how to implement the Business Delegate pattern. This pattern is particularly useful in applications requiring loose coupling and efficient service interaction.
+
+A mobile phone application promises to stream any movie in existence to your device. It captures the user's search string and passes this on to the Business Delegate. The Business Delegate selects the most suitable video streaming service and plays the video from there.
+
+First, we have an abstraction for video streaming services and a couple of implementations.
+
+```java
+public interface VideoStreamingService {
+ void doProcessing();
+}
+
+@Slf4j
+public class NetflixService implements VideoStreamingService {
+ @Override
+ public void doProcessing() {
+ LOGGER.info("NetflixService is now processing");
+ }
+}
+
+@Slf4j
+public class YouTubeService implements VideoStreamingService {
+ @Override
+ public void doProcessing() {
+ LOGGER.info("YouTubeService is now processing");
+ }
+}
+```
+
+Then, we have a lookup service that decides which video streaming service to use.
+
+```java
+
+@Setter
+public class BusinessLookup {
+
+ private NetflixService netflixService;
+ private YouTubeService youTubeService;
+
+ public VideoStreamingService getBusinessService(String movie) {
+ if (movie.toLowerCase(Locale.ROOT).contains("die hard")) {
+ return netflixService;
+ } else {
+ return youTubeService;
+ }
+ }
+}
+```
+
+The Business Delegate uses a business lookup to route movie playback requests to a suitable video streaming service.
+
+```java
+
+@Setter
+public class BusinessDelegate {
+
+ private BusinessLookup lookupService;
+
+ public void playbackMovie(String movie) {
+ VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie);
+ videoStreamingService.doProcessing();
+ }
+}
+```
+
+The mobile client utilizes Business Delegate to call the business tier.
+
+```java
+public class MobileClient {
+
+ private final BusinessDelegate businessDelegate;
+
+ public MobileClient(BusinessDelegate businessDelegate) {
+ this.businessDelegate = businessDelegate;
+ }
+
+ public void playbackMovie(String movie) {
+ businessDelegate.playbackMovie(movie);
+ }
+}
+```
+
+Finally, we can demonstrate the complete example in action.
+
+```java
+public static void main(String[] args) {
+
+ // prepare the objects
+ var businessDelegate = new BusinessDelegate();
+ var businessLookup = new BusinessLookup();
+ businessLookup.setNetflixService(new NetflixService());
+ businessLookup.setYouTubeService(new YouTubeService());
+ businessDelegate.setLookupService(businessLookup);
+
+ // create the client and use the business delegate
+ var client = new MobileClient(businessDelegate);
+ client.playbackMovie("Die Hard 2");
+ client.playbackMovie("Maradona: The Greatest Ever");
+}
+```
+
+Here is the console output.
+
+```
+21:15:33.790 [main] INFO com.iluwatar.business.delegate.NetflixService - NetflixService is now processing
+21:15:33.794 [main] INFO com.iluwatar.business.delegate.YouTubeService - YouTubeService is now processing
+```
+
+## When to Use the Business Delegate Pattern in Java
+
+Use the Business Delegate pattern when
+
+* You need loose coupling between presentation and business tiers or need to abstract service lookups.
+* You want to orchestrate calls to multiple business services
+* You want to encapsulate service lookups and service calls
+* There is a need to abstract and encapsulate the communication between the client tier and business services
+
+## Business Delegate Pattern Java Tutorials
+
+* [Design Patterns - Business Delegate Pattern (TutorialsPoint)](https://www.tutorialspoint.com/design_pattern/business_delegate_pattern.htm)
+
+## Real-World Applications of Business Delegate Pattern in Java
+
+* Enterprise applications using Java EE (Java Platform, Enterprise Edition)
+* Applications requiring remote access to business services
+
+## Benefits and Trade-offs of Business Delegate Pattern
+
+Benefits:
+
+* Decoupling of Presentation and Business Tiers: Allows the client tier and business services to evolve independently.
+* Location Transparency: Clients remain unaffected by changes in the location or the instantiation of business services.
+* Reuse and Scalability: Business Delegate objects can be reused by multiple clients, and the pattern supports load balancing and scalability.
+
+Trade-offs:
+
+* Complexity: Introduces additional layers and abstractions, which may increase complexity.
+* Performance Overhead: The additional indirection may incur a slight performance penalty.
+
+## Related Java Design Patterns
+
+* [Service Locator](https://java-design-patterns.com/patterns/service-locator/): Business Delegate uses Service Locator to locate business services.
+* [Session Facade](https://java-design-patterns.com/patterns/session-facade/): Business Delegate may use Session Facade to provide a unified interface to a set of business services.
+* [Composite Entity](https://java-design-patterns.com/patterns/composite-entity/): Business Delegate may use Composite Entity to manage the state of business services.
+
+## References and Credits
+
+* [Core J2EE Patterns: Best Practices and Design Strategies](https://amzn.to/4cAbDap)
+* [J2EE Design Patterns](https://amzn.to/4dpzgmx)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
diff --git a/business-delegate/etc/business-delegate-sequence-diagram.png b/business-delegate/etc/business-delegate-sequence-diagram.png
new file mode 100644
index 000000000000..a6279e9d1888
Binary files /dev/null and b/business-delegate/etc/business-delegate-sequence-diagram.png differ
diff --git a/business-delegate/etc/business-delegate.urm.png b/business-delegate/etc/business-delegate.urm.png
new file mode 100644
index 000000000000..4dca6c263b99
Binary files /dev/null and b/business-delegate/etc/business-delegate.urm.png differ
diff --git a/business-delegate/etc/business-delegate.urm.puml b/business-delegate/etc/business-delegate.urm.puml
new file mode 100644
index 000000000000..407e3e12d1ea
--- /dev/null
+++ b/business-delegate/etc/business-delegate.urm.puml
@@ -0,0 +1,46 @@
+@startuml
+package com.iluwatar.business.delegate {
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ class BusinessDelegate {
+ - lookupService : BusinessLookup
+ + BusinessDelegate()
+ + playbackMovie(movie : String)
+ + setLookupService(lookupService : BusinessLookup)
+ }
+ class BusinessLookup {
+ - netflixService : NetflixService
+ - youTubeService : YouTubeService
+ + BusinessLookup()
+ + getBusinessService(movie : String) : VideoStreamingService
+ + setNetflixService(netflixService : NetflixService)
+ + setYouTubeService(youTubeService : YouTubeService)
+ }
+ class MobileClient {
+ - businessDelegate : BusinessDelegate
+ + MobileClient(businessDelegate : BusinessDelegate)
+ + playbackMovie(movie : String)
+ }
+ class NetflixService {
+ - LOGGER : Logger {static}
+ + NetflixService()
+ + doProcessing()
+ }
+ interface VideoStreamingService {
+ + doProcessing() {abstract}
+ }
+ class YouTubeService {
+ - LOGGER : Logger {static}
+ + YouTubeService()
+ + doProcessing()
+ }
+}
+BusinessLookup --> "-netflixService" NetflixService
+BusinessLookup --> "-youTubeService" YouTubeService
+MobileClient --> "-businessDelegate" BusinessDelegate
+BusinessDelegate --> "-lookupService" BusinessLookup
+NetflixService ..|> VideoStreamingService
+YouTubeService ..|> VideoStreamingService
+@enduml
\ No newline at end of file
diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml
new file mode 100644
index 000000000000..a5bccb1529e7
--- /dev/null
+++ b/business-delegate/pom.xml
@@ -0,0 +1,75 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ business-delegate
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.business.delegate.App
+
+
+
+
+
+
+
+
+
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java
new file mode 100644
index 000000000000..c23ed42caecd
--- /dev/null
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java
@@ -0,0 +1,62 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.business.delegate;
+
+/**
+ * The Business Delegate pattern adds an abstraction layer between the presentation and business
+ * tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate
+ * encapsulates knowledge about how to locate, connect to, and interact with the business objects
+ * that make up the application.
+ *
+ *
Some of the services the Business Delegate uses are instantiated directly, and some can be
+ * retrieved through service lookups. The Business Delegate itself may contain business logic too
+ * potentially tying together multiple service calls, exception handling, retrying etc.
+ *
+ *
In this example the client ({@link MobileClient}) utilizes a business delegate ( {@link
+ * BusinessDelegate}) to search for movies in video streaming services. The Business Delegate then
+ * selects the appropriate service and makes the service call.
+ */
+public class App {
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+
+ // prepare the objects
+ var businessDelegate = new BusinessDelegate();
+ var businessLookup = new BusinessLookup();
+ businessLookup.setNetflixService(new NetflixService());
+ businessLookup.setYouTubeService(new YouTubeService());
+ businessDelegate.setLookupService(businessLookup);
+
+ // create the client and use the business delegate
+ var client = new MobileClient(businessDelegate);
+ client.playbackMovie("Die Hard 2");
+ client.playbackMovie("Maradona: The Greatest Ever");
+ }
+}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java
new file mode 100644
index 000000000000..81920c857cd8
--- /dev/null
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java
@@ -0,0 +1,39 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.business.delegate;
+
+import lombok.Setter;
+
+/** BusinessDelegate separates the presentation and business tiers. */
+@Setter
+public class BusinessDelegate {
+
+ private BusinessLookup lookupService;
+
+ public void playbackMovie(String movie) {
+ VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie);
+ videoStreamingService.doProcessing();
+ }
+}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java
new file mode 100644
index 000000000000..81a1b2f18fd0
--- /dev/null
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.business.delegate;
+
+import java.util.Locale;
+import lombok.Setter;
+
+/** Class for performing service lookups. */
+@Setter
+public class BusinessLookup {
+
+ private NetflixService netflixService;
+
+ private YouTubeService youTubeService;
+
+ /**
+ * Gets service instance based on given movie search string.
+ *
+ * @param movie Search string for the movie.
+ * @return Service instance.
+ */
+ public VideoStreamingService getBusinessService(String movie) {
+ if (movie.toLowerCase(Locale.ROOT).contains("die hard")) {
+ return netflixService;
+ } else {
+ return youTubeService;
+ }
+ }
+}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java
new file mode 100644
index 000000000000..01b5b642735b
--- /dev/null
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java
@@ -0,0 +1,39 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.business.delegate;
+
+/** MobileClient utilizes BusinessDelegate to call the business tier. */
+public class MobileClient {
+
+ private final BusinessDelegate businessDelegate;
+
+ public MobileClient(BusinessDelegate businessDelegate) {
+ this.businessDelegate = businessDelegate;
+ }
+
+ public void playbackMovie(String movie) {
+ businessDelegate.playbackMovie(movie);
+ }
+}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java
new file mode 100644
index 000000000000..696480e678f3
--- /dev/null
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.business.delegate;
+
+import lombok.extern.slf4j.Slf4j;
+
+/** NetflixService implementation. */
+@Slf4j
+public class NetflixService implements VideoStreamingService {
+
+ @Override
+ public void doProcessing() {
+ LOGGER.info("NetflixService is now processing");
+ }
+}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java
new file mode 100644
index 000000000000..594b51850efb
--- /dev/null
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java
@@ -0,0 +1,31 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.business.delegate;
+
+/** Interface for video streaming service implementations. */
+public interface VideoStreamingService {
+
+ void doProcessing();
+}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java
new file mode 100644
index 000000000000..65c2e55ff6fa
--- /dev/null
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.business.delegate;
+
+import lombok.extern.slf4j.Slf4j;
+
+/** YouTubeService implementation. */
+@Slf4j
+public class YouTubeService implements VideoStreamingService {
+
+ @Override
+ public void doProcessing() {
+ LOGGER.info("YouTubeService is now processing");
+ }
+}
diff --git a/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java b/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java
new file mode 100644
index 000000000000..5f862bf6e5ff
--- /dev/null
+++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java
@@ -0,0 +1,45 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.business.delegate;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests that Business Delegate example runs without errors. */
+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} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java
new file mode 100644
index 000000000000..4b8e0ee77d5c
--- /dev/null
+++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java
@@ -0,0 +1,86 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.business.delegate;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/** Tests for the {@link BusinessDelegate} */
+class BusinessDelegateTest {
+
+ private NetflixService netflixService;
+
+ private YouTubeService youTubeService;
+
+ private BusinessDelegate businessDelegate;
+
+ /**
+ * This method sets up the instance variables of this test class. It is executed before the
+ * execution of every test.
+ */
+ @BeforeEach
+ void setup() {
+ netflixService = spy(new NetflixService());
+ youTubeService = spy(new YouTubeService());
+
+ BusinessLookup businessLookup = spy(new BusinessLookup());
+ businessLookup.setNetflixService(netflixService);
+ businessLookup.setYouTubeService(youTubeService);
+
+ businessDelegate = spy(new BusinessDelegate());
+ businessDelegate.setLookupService(businessLookup);
+ }
+
+ /**
+ * In this example the client ({@link MobileClient}) utilizes a business delegate ( {@link
+ * BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
+ * service and makes the service call.
+ */
+ @Test
+ void testBusinessDelegate() {
+
+ // setup a client object
+ var client = new MobileClient(businessDelegate);
+
+ // action
+ client.playbackMovie("Die hard");
+
+ // verifying that the businessDelegate was used by client during playbackMovie() method.
+ verify(businessDelegate).playbackMovie(anyString());
+ verify(netflixService).doProcessing();
+
+ // action
+ client.playbackMovie("Maradona");
+
+ // verifying that the businessDelegate was used by client during doTask() method.
+ verify(businessDelegate, times(2)).playbackMovie(anyString());
+ verify(youTubeService).doProcessing();
+ }
+}
diff --git a/bytecode/README.md b/bytecode/README.md
new file mode 100644
index 000000000000..793eecffcc0e
--- /dev/null
+++ b/bytecode/README.md
@@ -0,0 +1,270 @@
+---
+title: "Bytecode Pattern in Java: Interpreting Instructions with Custom Virtual Machines"
+shortTitle: Bytecode
+description: "Explore the Bytecode design pattern in Java, including its implementation, real-world examples, and use cases for efficient virtual machine instruction handling."
+category: Behavioral
+language: en
+tag:
+ - Abstraction
+ - Code simplification
+ - Encapsulation
+ - Game programming
+ - Performance
+ - Runtime
+---
+
+## Intent of Bytecode Design Pattern
+
+The Bytecode design pattern in Java allows encoding behavior as instructions for a virtual machine, making it a powerful tool in game development and other applications.
+
+## Detailed Explanation of Bytecode Pattern with Real-World Examples
+
+Real-world example
+
+> An analogous real-world example of the Bytecode design pattern can be seen in the process of translating a book into multiple languages. Instead of directly translating the book from the original language into every other language, the book is first translated into a common intermediate language, like Esperanto. This intermediate version is easier to translate because it is simpler and more structured. Translators for each target language then translate from Esperanto into their specific languages. This approach ensures consistency, reduces errors, and simplifies the translation process, similar to how bytecode serves as an intermediate representation to optimize and facilitate the execution of high-level programming languages across different platforms.
+
+In plain words
+
+> Bytecode pattern enables behavior driven by data instead of code.
+
+[gameprogrammingpatterns.com](https://gameprogrammingpatterns.com/bytecode.html) documentation states:
+
+> An instruction set defines the low-level operations that can be performed. A series of instructions is encoded as a sequence of bytes. A virtual machine executes these instructions one at a time, using a stack for intermediate values. By combining instructions, complex high-level behavior can be defined.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Bytecode Pattern in Java
+
+In this programmatic example, we show how the Bytecode pattern in Java can simplify the execution of complex virtual machine instructions through a well-defined set of operations. This real-world example demonstrates how the Bytecode design pattern in Java can streamline game programming by allowing wizards' behavior to be easily adjusted through bytecode instructions.
+
+A team is working on a new game where wizards battle against each other. The wizard behavior needs to be carefully adjusted and iterated hundreds of times through playtesting. It's not optimal to ask the programmer to make changes each time the game designer wants to vary the behavior, so the wizard behavior is implemented as a data-driven virtual machine.
+
+One of the most important game objects is the `Wizard` class.
+
+```java
+
+@AllArgsConstructor
+@Setter
+@Getter
+@Slf4j
+public class Wizard {
+
+ private int health;
+ private int agility;
+ private int wisdom;
+ private int numberOfPlayedSounds;
+ private int numberOfSpawnedParticles;
+
+ public void playSound() {
+ LOGGER.info("Playing sound");
+ numberOfPlayedSounds++;
+ }
+
+ public void spawnParticles() {
+ LOGGER.info("Spawning particles");
+ numberOfSpawnedParticles++;
+ }
+}
+```
+
+Next, we show the available instructions for our virtual machine. Each of the instructions has its own semantics on how it operates with the stack data. For example, the ADD instruction takes the top two items from the stack, adds them together and pushes the result to the stack.
+
+```java
+
+@AllArgsConstructor
+@Getter
+public enum Instruction {
+
+ LITERAL(1), // e.g. "LITERAL 0", push 0 to stack
+ SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health
+ SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom
+ SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility
+ PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound
+ SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles
+ GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health
+ GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility
+ GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom
+ ADD(10), // e.g. "ADD", pop 2 values, push their sum
+ DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division
+
+ // Other properties and methods...
+}
+```
+
+At the heart of our example is the `VirtualMachine` class. It takes instructions as input and executes them to provide the game object behavior.
+
+```java
+
+@Getter
+@Slf4j
+public class VirtualMachine {
+
+ private final Stack stack = new Stack<>();
+
+ private final Wizard[] wizards = new Wizard[2];
+
+ public VirtualMachine() {
+ wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
+ 0, 0);
+ wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
+ 0, 0);
+ }
+
+ public VirtualMachine(Wizard wizard1, Wizard wizard2) {
+ wizards[0] = wizard1;
+ wizards[1] = wizard2;
+ }
+
+ public void execute(int[] bytecode) {
+ for (var i = 0; i < bytecode.length; i++) {
+ Instruction instruction = Instruction.getInstruction(bytecode[i]);
+ switch (instruction) {
+ case LITERAL:
+ // Read the next byte from the bytecode.
+ int value = bytecode[++i];
+ // Push the next value to stack
+ stack.push(value);
+ break;
+ case SET_AGILITY:
+ var amount = stack.pop();
+ var wizard = stack.pop();
+ setAgility(wizard, amount);
+ break;
+ case SET_WISDOM:
+ amount = stack.pop();
+ wizard = stack.pop();
+ setWisdom(wizard, amount);
+ break;
+ case SET_HEALTH:
+ amount = stack.pop();
+ wizard = stack.pop();
+ setHealth(wizard, amount);
+ break;
+ case GET_HEALTH:
+ wizard = stack.pop();
+ stack.push(getHealth(wizard));
+ break;
+ case GET_AGILITY:
+ wizard = stack.pop();
+ stack.push(getAgility(wizard));
+ break;
+ case GET_WISDOM:
+ wizard = stack.pop();
+ stack.push(getWisdom(wizard));
+ break;
+ case ADD:
+ var a = stack.pop();
+ var b = stack.pop();
+ stack.push(a + b);
+ break;
+ case DIVIDE:
+ a = stack.pop();
+ b = stack.pop();
+ stack.push(b / a);
+ break;
+ case PLAY_SOUND:
+ wizard = stack.pop();
+ getWizards()[wizard].playSound();
+ break;
+ case SPAWN_PARTICLES:
+ wizard = stack.pop();
+ getWizards()[wizard].spawnParticles();
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid instruction value");
+ }
+ LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack());
+ }
+ }
+
+ public void setHealth(int wizard, int amount) {
+ wizards[wizard].setHealth(amount);
+ }
+
+ // Other properties and methods...
+}
+```
+
+Now we can show the full example utilizing the virtual machine.
+
+```java
+public static void main(String[] args) {
+
+ var vm = new VirtualMachine(
+ new Wizard(45, 7, 11, 0, 0),
+ new Wizard(36, 18, 8, 0, 0));
+
+ vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
+ vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
+ vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "GET")));
+ vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
+ vm.execute(InstructionConverterUtil.convertToByteCode(GET_AGILITY));
+ vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
+ vm.execute(InstructionConverterUtil.convertToByteCode(GET_WISDOM));
+ vm.execute(InstructionConverterUtil.convertToByteCode(ADD));
+ vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_2));
+ vm.execute(InstructionConverterUtil.convertToByteCode(DIVIDE));
+ vm.execute(InstructionConverterUtil.convertToByteCode(ADD));
+ vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "SET")));
+}
+```
+
+Here is the console output.
+
+```
+16:20:10.193 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0]
+16:20:10.196 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 0]
+16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_HEALTH, Stack contains [0, 45]
+16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 0]
+16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_AGILITY, Stack contains [0, 45, 7]
+16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 7, 0]
+16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_WISDOM, Stack contains [0, 45, 7, 11]
+16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 45, 18]
+16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 18, 2]
+16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed DIVIDE, Stack contains [0, 45, 9]
+16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 54]
+16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed SET_HEALTH, Stack contains []
+```
+
+Utilizing the Bytecode design pattern in Java can significantly enhance the flexibility and maintainability of your virtual machine-based applications.
+
+## When to Use the Bytecode Pattern in Java
+
+Use the Bytecode pattern when you have a lot of behavior you need to define and your game’s implementation language isn’t a good fit because:
+
+* It’s too low-level, making it tedious or error-prone to program in.
+* Iterating on it takes too long due to slow compile times or other tooling issues.
+* It has too much trust. If you want to ensure the behavior being defined can’t break the game, you need to sandbox it from the rest of the codebase.
+
+## Real-World Applications of Bytecode Pattern in Java
+
+* Java Virtual Machine (JVM) uses bytecode to allow Java programs to run on any device that has JVM installed
+* Python compiles its scripts to bytecode which is then interpreted by Python Virtual Machine
+* The .NET Framework uses a form of bytecode called Microsoft Intermediate Language (MSIL)
+
+## Benefits and Trade-offs of Bytecode Pattern
+
+Benefits:
+
+* Portability: Programs can run on any platform that has a compatible VM.
+* Security: The VM can enforce security checks on the bytecode.
+* Performance: JIT compilers can optimize bytecode at runtime, potentially improving performance over interpreted code.
+
+Trade-offs:
+
+* Overhead: Running bytecode typically involves more overhead than running native code, potentially affecting performance.
+* Complexity: Implementing and maintaining a VM adds complexity to the system.
+
+## Related Java Design Patterns
+
+* [Interpreter](https://java-design-patterns.com/patterns/interpreter/) is often used within the implementation of VMs to interpret bytecode instructions
+* [Command](https://java-design-patterns.com/patterns/command/): Bytecode instructions can be seen as commands executed by the VM.
+* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): VMs may use factory methods to instantiate operations or instructions defined in the bytecode.
+
+## References and Credits
+
+* [Game Programming Patterns](https://amzn.to/3K96fOn)
+* [Programming Language Pragmatics](https://amzn.to/49Tusnn)
+* [Bytecode (Game Programming Patterns)](http://gameprogrammingpatterns.com/bytecode.html)
diff --git a/bytecode/etc/bytecode-sequence-diagram.png b/bytecode/etc/bytecode-sequence-diagram.png
new file mode 100644
index 000000000000..c863a0e0b5cd
Binary files /dev/null and b/bytecode/etc/bytecode-sequence-diagram.png differ
diff --git a/bytecode/etc/bytecode.urm.png b/bytecode/etc/bytecode.urm.png
new file mode 100644
index 000000000000..51335fa0a4b4
Binary files /dev/null and b/bytecode/etc/bytecode.urm.png differ
diff --git a/bytecode/etc/bytecode.urm.puml b/bytecode/etc/bytecode.urm.puml
new file mode 100644
index 000000000000..224e909efa15
--- /dev/null
+++ b/bytecode/etc/bytecode.urm.puml
@@ -0,0 +1,73 @@
+@startuml
+package com.iluwatar.bytecode {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ + main(args : String[]) {static}
+ }
+ enum Instruction {
+ + ADD {static}
+ + DIVIDE {static}
+ + GET_AGILITY {static}
+ + GET_HEALTH {static}
+ + GET_WISDOM {static}
+ + LITERAL {static}
+ + PLAY_SOUND {static}
+ + SET_AGILITY {static}
+ + SET_HEALTH {static}
+ + SET_WISDOM {static}
+ + SPAWN_PARTICLES {static}
+ - intValue : int
+ + getInstruction(value : int) : Instruction {static}
+ + getIntValue() : int
+ + valueOf(name : String) : Instruction {static}
+ + values() : Instruction[] {static}
+ }
+ class VirtualMachine {
+ - LOGGER : Logger {static}
+ - stack : Stack
+ - wizards : Wizard[]
+ + VirtualMachine()
+ + VirtualMachine(wizard1 : Wizard, wizard2 : Wizard)
+ + execute(bytecode : int[])
+ + getAgility(wizard : int) : int
+ + getHealth(wizard : int) : int
+ + getStack() : Stack
+ + getWisdom(wizard : int) : int
+ + getWizards() : Wizard[]
+ - randomInt(min : int, max : int) : int
+ + setAgility(wizard : int, amount : int)
+ + setHealth(wizard : int, amount : int)
+ + setWisdom(wizard : int, amount : int)
+ }
+ class Wizard {
+ - LOGGER : Logger {static}
+ - agility : int
+ - health : int
+ - numberOfPlayedSounds : int
+ - numberOfSpawnedParticles : int
+ - wisdom : int
+ + Wizard(health : int, agility : int, wisdom : int, numberOfPlayedSounds : int, numberOfSpawnedParticles : int)
+ + getAgility() : int
+ + getHealth() : int
+ + getNumberOfPlayedSounds() : int
+ + getNumberOfSpawnedParticles() : int
+ + getWisdom() : int
+ + playSound()
+ + setAgility(agility : int)
+ + setHealth(health : int)
+ + setNumberOfPlayedSounds(numberOfPlayedSounds : int)
+ + setNumberOfSpawnedParticles(numberOfSpawnedParticles : int)
+ + setWisdom(wisdom : int)
+ + spawnParticles()
+ }
+}
+package com.iluwatar.bytecode.util {
+ class InstructionConverterUtil {
+ + InstructionConverterUtil()
+ + convertToByteCode(instructions : String) : int[] {static}
+ - isValidInstruction(instruction : String) : boolean {static}
+ - isValidInt(value : String) : boolean {static}
+ }
+}
+@enduml
\ No newline at end of file
diff --git a/bytecode/pom.xml b/bytecode/pom.xml
new file mode 100644
index 000000000000..2fb01fe2f914
--- /dev/null
+++ b/bytecode/pom.xml
@@ -0,0 +1,70 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ bytecode
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.bytecode.App
+
+
+
+
+
+
+
+
+
diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/App.java b/bytecode/src/main/java/com/iluwatar/bytecode/App.java
new file mode 100644
index 000000000000..9293b5876ef5
--- /dev/null
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/App.java
@@ -0,0 +1,76 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bytecode;
+
+import com.iluwatar.bytecode.util.InstructionConverterUtil;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * The intention of Bytecode pattern is to give behavior the flexibility of data by encoding it as
+ * instructions for a virtual machine. An instruction set defines the low-level operations that can
+ * be performed. A series of instructions is encoded as a sequence of bytes. A virtual machine
+ * executes these instructions one at a time, using a stack for intermediate values. By combining
+ * instructions, complex high-level behavior can be defined.
+ *
+ * This pattern should be used when there is a need to define high number of behaviours and
+ * implementation engine is not a good choice because It is too lowe level Iterating on it takes too
+ * long due to slow compile times or other tooling issues. It has too much trust. If you want to
+ * ensure the behavior being defined can’t break the game, you need to sandbox it from the rest of
+ * the codebase.
+ */
+@Slf4j
+public class App {
+
+ private static final String LITERAL_0 = "LITERAL 0";
+ private static final String HEALTH_PATTERN = "%s_HEALTH";
+ private static final String GET_AGILITY = "GET_AGILITY";
+ private static final String GET_WISDOM = "GET_WISDOM";
+ private static final String ADD = "ADD";
+ private static final String LITERAL_2 = "LITERAL 2";
+ private static final String DIVIDE = "DIVIDE";
+
+ /**
+ * Main app method.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+
+ var vm = new VirtualMachine(new Wizard(45, 7, 11, 0, 0), new Wizard(36, 18, 8, 0, 0));
+
+ vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
+ vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
+ vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "GET")));
+ vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
+ vm.execute(InstructionConverterUtil.convertToByteCode(GET_AGILITY));
+ vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
+ vm.execute(InstructionConverterUtil.convertToByteCode(GET_WISDOM));
+ vm.execute(InstructionConverterUtil.convertToByteCode(ADD));
+ vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_2));
+ vm.execute(InstructionConverterUtil.convertToByteCode(DIVIDE));
+ vm.execute(InstructionConverterUtil.convertToByteCode(ADD));
+ vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "SET")));
+ }
+}
diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java b/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java
new file mode 100644
index 000000000000..25330e73fd78
--- /dev/null
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java
@@ -0,0 +1,62 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bytecode;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/** Representation of instructions understandable by virtual machine. */
+@AllArgsConstructor
+@Getter
+public enum Instruction {
+ LITERAL(1), // e.g. "LITERAL 0", push 0 to stack
+ SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health
+ SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom
+ SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility
+ PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound
+ SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles
+ GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health
+ GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility
+ GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom
+ ADD(10), // e.g. "ADD", pop 2 values, push their sum
+ DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division
+
+ private final int intValue;
+
+ /**
+ * Converts integer value to Instruction.
+ *
+ * @param value value of instruction
+ * @return representation of the instruction
+ */
+ public static Instruction getInstruction(int value) {
+ for (var i = 0; i < Instruction.values().length; i++) {
+ if (Instruction.values()[i].getIntValue() == value) {
+ return Instruction.values()[i];
+ }
+ }
+ throw new IllegalArgumentException("Invalid instruction value");
+ }
+}
diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java b/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java
new file mode 100644
index 000000000000..7f835d402f15
--- /dev/null
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java
@@ -0,0 +1,147 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bytecode;
+
+import java.util.Stack;
+import java.util.concurrent.ThreadLocalRandom;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+/** Implementation of virtual machine. */
+@Getter
+@Slf4j
+public class VirtualMachine {
+
+ private final Stack stack = new Stack<>();
+
+ private final Wizard[] wizards = new Wizard[2];
+
+ /** No-args constructor. */
+ public VirtualMachine() {
+ wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), 0, 0);
+ wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), 0, 0);
+ }
+
+ /** Constructor taking the wizards as arguments. */
+ public VirtualMachine(Wizard wizard1, Wizard wizard2) {
+ wizards[0] = wizard1;
+ wizards[1] = wizard2;
+ }
+
+ /**
+ * Executes provided bytecode.
+ *
+ * @param bytecode to execute
+ */
+ public void execute(int[] bytecode) {
+ for (var i = 0; i < bytecode.length; i++) {
+ Instruction instruction = Instruction.getInstruction(bytecode[i]);
+ switch (instruction) {
+ case LITERAL -> { // Read the next byte from the bytecode.
+ int value = bytecode[++i];
+ // Push the next value to stack
+ stack.push(value);
+ }
+ case SET_AGILITY -> {
+ var amount = stack.pop();
+ var wizard = stack.pop();
+ setAgility(wizard, amount);
+ }
+ case SET_WISDOM -> {
+ var amount = stack.pop();
+ var wizard = stack.pop();
+ setWisdom(wizard, amount);
+ }
+ case SET_HEALTH -> {
+ var amount = stack.pop();
+ var wizard = stack.pop();
+ setHealth(wizard, amount);
+ }
+ case GET_HEALTH -> {
+ var wizard = stack.pop();
+ stack.push(getHealth(wizard));
+ }
+ case GET_AGILITY -> {
+ var wizard = stack.pop();
+ stack.push(getAgility(wizard));
+ }
+ case GET_WISDOM -> {
+ var wizard = stack.pop();
+ stack.push(getWisdom(wizard));
+ }
+ case ADD -> {
+ var a = stack.pop();
+ var b = stack.pop();
+ stack.push(a + b);
+ }
+ case DIVIDE -> {
+ var a = stack.pop();
+ var b = stack.pop();
+ stack.push(b / a);
+ }
+ case PLAY_SOUND -> {
+ var wizard = stack.pop();
+ getWizards()[wizard].playSound();
+ }
+ case SPAWN_PARTICLES -> {
+ var wizard = stack.pop();
+ getWizards()[wizard].spawnParticles();
+ }
+ default -> {
+ throw new IllegalArgumentException("Invalid instruction value");
+ }
+ }
+ LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack());
+ }
+ }
+
+ public void setHealth(int wizard, int amount) {
+ wizards[wizard].setHealth(amount);
+ }
+
+ public void setWisdom(int wizard, int amount) {
+ wizards[wizard].setWisdom(amount);
+ }
+
+ public void setAgility(int wizard, int amount) {
+ wizards[wizard].setAgility(amount);
+ }
+
+ public int getHealth(int wizard) {
+ return wizards[wizard].getHealth();
+ }
+
+ public int getWisdom(int wizard) {
+ return wizards[wizard].getWisdom();
+ }
+
+ public int getAgility(int wizard) {
+ return wizards[wizard].getAgility();
+ }
+
+ private int randomInt(int min, int max) {
+ return ThreadLocalRandom.current().nextInt(min, max + 1);
+ }
+}
diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java b/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java
new file mode 100644
index 000000000000..8b8bfac9f98b
--- /dev/null
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java
@@ -0,0 +1,57 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bytecode;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * This class represent game objects which properties can be changed by instructions interpreted by
+ * virtual machine.
+ */
+@AllArgsConstructor
+@Setter
+@Getter
+@Slf4j
+public class Wizard {
+
+ private int health;
+ private int agility;
+ private int wisdom;
+ private int numberOfPlayedSounds;
+ private int numberOfSpawnedParticles;
+
+ public void playSound() {
+ LOGGER.info("Playing sound");
+ numberOfPlayedSounds++;
+ }
+
+ public void spawnParticles() {
+ LOGGER.info("Spawning particles");
+ numberOfSpawnedParticles++;
+ }
+}
diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java b/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java
new file mode 100644
index 000000000000..d45a2aa55787
--- /dev/null
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java
@@ -0,0 +1,75 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bytecode.util;
+
+import com.iluwatar.bytecode.Instruction;
+
+/** Utility class used for instruction validation and conversion. */
+public class InstructionConverterUtil {
+ /**
+ * Converts instructions represented as String.
+ *
+ * @param instructions to convert
+ * @return array of int representing bytecode
+ */
+ public static int[] convertToByteCode(String instructions) {
+ if (instructions == null || instructions.trim().length() == 0) {
+ return new int[0];
+ }
+
+ var splitedInstructions = instructions.trim().split(" ");
+ var bytecode = new int[splitedInstructions.length];
+ for (var i = 0; i < splitedInstructions.length; i++) {
+ if (isValidInstruction(splitedInstructions[i])) {
+ bytecode[i] = Instruction.valueOf(splitedInstructions[i]).getIntValue();
+ } else if (isValidInt(splitedInstructions[i])) {
+ bytecode[i] = Integer.parseInt(splitedInstructions[i]);
+ } else {
+ var errorMessage = "Invalid instruction or number: " + splitedInstructions[i];
+ throw new IllegalArgumentException(errorMessage);
+ }
+ }
+
+ return bytecode;
+ }
+
+ private static boolean isValidInstruction(String instruction) {
+ try {
+ Instruction.valueOf(instruction);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+
+ private static boolean isValidInt(String value) {
+ try {
+ Integer.parseInt(value);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+}
diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java
new file mode 100644
index 000000000000..72d00eb34fb3
--- /dev/null
+++ b/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java
@@ -0,0 +1,44 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bytecode;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Application 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} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java
new file mode 100644
index 000000000000..1d9a5539f51b
--- /dev/null
+++ b/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java
@@ -0,0 +1,154 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bytecode;
+
+import static com.iluwatar.bytecode.Instruction.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link VirtualMachine} */
+class VirtualMachineTest {
+
+ @Test
+ void testLiteral() {
+ var bytecode = new int[2];
+ bytecode[0] = LITERAL.getIntValue();
+ bytecode[1] = 10;
+
+ var vm = new VirtualMachine();
+ vm.execute(bytecode);
+
+ assertEquals(1, vm.getStack().size());
+ assertEquals(Integer.valueOf(10), vm.getStack().pop());
+ }
+
+ @Test
+ void testSetHealth() {
+ var wizardNumber = 0;
+ var bytecode = new int[5];
+ bytecode[0] = LITERAL.getIntValue();
+ bytecode[1] = wizardNumber;
+ bytecode[2] = LITERAL.getIntValue();
+ bytecode[3] = 50; // health amount
+ bytecode[4] = SET_HEALTH.getIntValue();
+
+ var vm = new VirtualMachine();
+ vm.execute(bytecode);
+
+ assertEquals(50, vm.getWizards()[wizardNumber].getHealth());
+ }
+
+ @Test
+ void testSetAgility() {
+ var wizardNumber = 0;
+ var bytecode = new int[5];
+ bytecode[0] = LITERAL.getIntValue();
+ bytecode[1] = wizardNumber;
+ bytecode[2] = LITERAL.getIntValue();
+ bytecode[3] = 50; // agility amount
+ bytecode[4] = SET_AGILITY.getIntValue();
+
+ var vm = new VirtualMachine();
+ vm.execute(bytecode);
+
+ assertEquals(50, vm.getWizards()[wizardNumber].getAgility());
+ }
+
+ @Test
+ void testSetWisdom() {
+ var wizardNumber = 0;
+ var bytecode = new int[5];
+ bytecode[0] = LITERAL.getIntValue();
+ bytecode[1] = wizardNumber;
+ bytecode[2] = LITERAL.getIntValue();
+ bytecode[3] = 50; // wisdom amount
+ bytecode[4] = SET_WISDOM.getIntValue();
+
+ var vm = new VirtualMachine();
+ vm.execute(bytecode);
+
+ assertEquals(50, vm.getWizards()[wizardNumber].getWisdom());
+ }
+
+ @Test
+ void testGetHealth() {
+ var wizardNumber = 0;
+ var bytecode = new int[8];
+ bytecode[0] = LITERAL.getIntValue();
+ bytecode[1] = wizardNumber;
+ bytecode[2] = LITERAL.getIntValue();
+ bytecode[3] = 50; // health amount
+ bytecode[4] = SET_HEALTH.getIntValue();
+ bytecode[5] = LITERAL.getIntValue();
+ bytecode[6] = wizardNumber;
+ bytecode[7] = GET_HEALTH.getIntValue();
+
+ var vm = new VirtualMachine();
+ vm.execute(bytecode);
+
+ assertEquals(Integer.valueOf(50), vm.getStack().pop());
+ }
+
+ @Test
+ void testPlaySound() {
+ var wizardNumber = 0;
+ var bytecode = new int[3];
+ bytecode[0] = LITERAL.getIntValue();
+ bytecode[1] = wizardNumber;
+ bytecode[2] = PLAY_SOUND.getIntValue();
+
+ var vm = new VirtualMachine();
+ vm.execute(bytecode);
+
+ assertEquals(0, vm.getStack().size());
+ assertEquals(1, vm.getWizards()[0].getNumberOfPlayedSounds());
+ }
+
+ @Test
+ void testSpawnParticles() {
+ var wizardNumber = 0;
+ var bytecode = new int[3];
+ bytecode[0] = LITERAL.getIntValue();
+ bytecode[1] = wizardNumber;
+ bytecode[2] = SPAWN_PARTICLES.getIntValue();
+
+ var vm = new VirtualMachine();
+ vm.execute(bytecode);
+
+ assertEquals(0, vm.getStack().size());
+ assertEquals(1, vm.getWizards()[0].getNumberOfSpawnedParticles());
+ }
+
+ @Test
+ void testInvalidInstruction() {
+ var bytecode = new int[1];
+ bytecode[0] = 999;
+ var vm = new VirtualMachine();
+
+ assertThrows(IllegalArgumentException.class, () -> vm.execute(bytecode));
+ }
+}
diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java
new file mode 100644
index 000000000000..9dadba1eaf21
--- /dev/null
+++ b/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java
@@ -0,0 +1,63 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.bytecode.util;
+
+import com.iluwatar.bytecode.Instruction;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link InstructionConverterUtil} */
+class InstructionConverterUtilTest {
+
+ @Test
+ void testEmptyInstruction() {
+ var instruction = "";
+
+ var bytecode = InstructionConverterUtil.convertToByteCode(instruction);
+
+ Assertions.assertEquals(0, bytecode.length);
+ }
+
+ @Test
+ void testInstructions() {
+ var instructions =
+ "LITERAL 35 SET_HEALTH SET_WISDOM SET_AGILITY PLAY_SOUND"
+ + " SPAWN_PARTICLES GET_HEALTH ADD DIVIDE";
+
+ var bytecode = InstructionConverterUtil.convertToByteCode(instructions);
+
+ Assertions.assertEquals(10, bytecode.length);
+ Assertions.assertEquals(Instruction.LITERAL.getIntValue(), bytecode[0]);
+ Assertions.assertEquals(35, bytecode[1]);
+ Assertions.assertEquals(Instruction.SET_HEALTH.getIntValue(), bytecode[2]);
+ Assertions.assertEquals(Instruction.SET_WISDOM.getIntValue(), bytecode[3]);
+ Assertions.assertEquals(Instruction.SET_AGILITY.getIntValue(), bytecode[4]);
+ Assertions.assertEquals(Instruction.PLAY_SOUND.getIntValue(), bytecode[5]);
+ Assertions.assertEquals(Instruction.SPAWN_PARTICLES.getIntValue(), bytecode[6]);
+ Assertions.assertEquals(Instruction.GET_HEALTH.getIntValue(), bytecode[7]);
+ Assertions.assertEquals(Instruction.ADD.getIntValue(), bytecode[8]);
+ Assertions.assertEquals(Instruction.DIVIDE.getIntValue(), bytecode[9]);
+ }
+}
diff --git a/caching/.gitignore b/caching/.gitignore
new file mode 100644
index 000000000000..b83d22266ac8
--- /dev/null
+++ b/caching/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/caching/README.md b/caching/README.md
new file mode 100644
index 000000000000..2e9c3761ef8d
--- /dev/null
+++ b/caching/README.md
@@ -0,0 +1,478 @@
+---
+title: "Caching Pattern in Java: Accelerating Data Access Speeds"
+shortTitle: Caching
+description: "Learn how to optimize performance with the Java Caching Design Pattern. Explore various caching strategies, real-world examples, and implementation techniques for efficient resource management."
+category: Performance optimization
+language: en
+tag:
+ - Caching
+ - Data access
+ - Performance
+ - Resource management
+---
+
+## Also known as
+
+* Cache
+* Temporary Storage
+
+## Intent of Caching Design Pattern
+
+The Java Caching Design Pattern is crucial for performance optimization and resource management. It involves various caching strategies such as write-through, read-through, and LRU cache to ensure efficient data access. The caching pattern avoids expensive re-acquisition of resources by not releasing them immediately after use. The resources retain their identity, are kept in some fast-access storage, and are re-used to avoid having to acquire them again.
+
+## Detailed Explanation of Caching Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world example of the Caching Design Pattern in Java is a library's catalog system. By caching frequently searched book results, the system reduces database load and enhances performance. When patrons frequently search for popular books, the system can cache the results of these searches. Instead of querying the database every time a user searches for a popular book, the system quickly retrieves the results from the cache. This reduces the load on the database and provides faster response times for users, enhancing their overall experience. However, the system must also ensure that the cache is updated when new books are added or existing ones are checked out, to maintain accurate information.
+
+In plain words
+
+> Caching pattern keeps frequently needed data in fast-access storage to improve performance.
+
+Wikipedia says
+
+> In computing, a cache is a hardware or software component that stores data so that future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it cannot. Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading from a slower data store; thus, the more requests that can be served from the cache, the faster the system performs.
+
+Flowchart
+
+
+
+## Programmatic Example of Caching Pattern in Java
+
+In this programmatic example, we demonstrate different Java caching strategies, including write-through, write-around, and write-behind, using a user account management system.
+
+A team is working on a website that provides new homes for abandoned cats. People can post their cats on the website after registering, but all the new posts require approval from one of the site moderators. The user accounts of the site moderators contain a specific flag and the data is stored in a MongoDB database. Checking for the moderator flag each time a post is viewed becomes expensive, and it's a good idea to utilize caching here.
+
+Let's first look at the data layer of our application. The interesting classes are `UserAccount` which is a simple Java object containing the user account details, and `DbManager` interface which handles reading and writing of these objects to/from database.
+
+```java
+
+@Data
+@AllArgsConstructor
+@ToString
+@EqualsAndHashCode
+public class UserAccount {
+ private String userId;
+ private String userName;
+ private String additionalInfo;
+}
+
+public interface DbManager {
+
+ void connect();
+
+ void disconnect();
+
+ UserAccount readFromDb(String userId);
+
+ UserAccount writeToDb(UserAccount userAccount);
+
+ UserAccount updateDb(UserAccount userAccount);
+
+ UserAccount upsertDb(UserAccount userAccount);
+}
+```
+
+In the example, we are demonstrating various different caching policies. The following caching strategies are implemented in Java: Write-through, Write-around, Write-behind, and Cache-aside. Each strategy offers unique benefits for improving performance and reducing load on the database.
+
+* Write-through writes data to the cache and DB in a single transaction
+* Write-around writes data immediately into the DB instead of the cache
+* Write-behind writes data into the cache initially whilst the data is only written into the DB when the cache is full
+* Cache-aside pushes the responsibility of keeping the data synchronized in both data sources to the application itself
+* Read-through strategy is also included in the aforementioned strategies, and it returns data from the cache to the caller if it exists, otherwise queries from DB and stores it into the cache for future use.
+
+The cache implementation in `LruCache` is a hash table accompanied by a doubly linked-list. The linked-list helps in capturing and maintaining the LRU data in the cache. When data is queried (from the cache), added (to the cache), or updated, the data is moved to the front of the list to depict itself as the most-recently-used data. The LRU data is always at the end of the list.
+
+```java
+
+@Slf4j
+public class LruCache {
+
+ static class Node {
+ String userId;
+ UserAccount userAccount;
+ Node previous;
+ Node next;
+
+ public Node(String userId, UserAccount userAccount) {
+ this.userId = userId;
+ this.userAccount = userAccount;
+ }
+ }
+
+ // Other properties and methods...
+
+ public LruCache(int capacity) {
+ this.capacity = capacity;
+ }
+
+ public UserAccount get(String userId) {
+ if (cache.containsKey(userId)) {
+ var node = cache.get(userId);
+ remove(node);
+ setHead(node);
+ return node.userAccount;
+ }
+ return null;
+ }
+
+ public void set(String userId, UserAccount userAccount) {
+ if (cache.containsKey(userId)) {
+ var old = cache.get(userId);
+ old.userAccount = userAccount;
+ remove(old);
+ setHead(old);
+ } else {
+ var newNode = new Node(userId, userAccount);
+ if (cache.size() >= capacity) {
+ LOGGER.info("# Cache is FULL! Removing {} from cache...", end.userId);
+ cache.remove(end.userId); // remove LRU data from cache.
+ remove(end);
+ setHead(newNode);
+ } else {
+ setHead(newNode);
+ }
+ cache.put(userId, newNode);
+ }
+ }
+
+ public boolean contains(String userId) {
+ return cache.containsKey(userId);
+ }
+
+ public void remove(Node node) { /* ... */ }
+
+ public void setHead(Node node) { /* ... */ }
+
+ public void invalidate(String userId) { /* ... */ }
+
+ public boolean isFull() { /* ... */ }
+
+ public UserAccount getLruData() { /* ... */ }
+
+ public void clear() { /* ... */ }
+
+ public List getCacheDataInListForm() { /* ... */ }
+
+ public void setCapacity(int newCapacity) { /* ... */ }
+}
+```
+
+The next layer we are going to look at is `CacheStore` which implements the different caching strategies.
+
+```java
+
+@Slf4j
+public class CacheStore {
+
+ private static final int CAPACITY = 3;
+ private static LruCache cache;
+ private final DbManager dbManager;
+
+ // Other properties and methods...
+
+ public UserAccount readThrough(final String userId) {
+ if (cache.contains(userId)) {
+ LOGGER.info("# Found in Cache!");
+ return cache.get(userId);
+ }
+ LOGGER.info("# Not found in cache! Go to DB!!");
+ UserAccount userAccount = dbManager.readFromDb(userId);
+ cache.set(userId, userAccount);
+ return userAccount;
+ }
+
+ public void writeThrough(final UserAccount userAccount) {
+ if (cache.contains(userAccount.getUserId())) {
+ dbManager.updateDb(userAccount);
+ } else {
+ dbManager.writeToDb(userAccount);
+ }
+ cache.set(userAccount.getUserId(), userAccount);
+ }
+
+ public void writeAround(final UserAccount userAccount) {
+ if (cache.contains(userAccount.getUserId())) {
+ dbManager.updateDb(userAccount);
+ // Cache data has been updated -- remove older
+ cache.invalidate(userAccount.getUserId());
+ // version from cache.
+ } else {
+ dbManager.writeToDb(userAccount);
+ }
+ }
+
+ public static void clearCache() {
+ if (cache != null) {
+ cache.clear();
+ }
+ }
+
+ public static void flushCache() {
+ LOGGER.info("# flushCache...");
+ Optional.ofNullable(cache)
+ .map(LruCache::getCacheDataInListForm)
+ .orElse(List.of())
+ .forEach(DbManager::updateDb);
+ }
+
+ // ... omitted the implementation of other caching strategies ...
+
+}
+```
+
+`AppManager` helps to bridge the gap in communication between the main class and the application's back-end. DB connection is initialized through this class. The chosen caching strategy/policy is also initialized here. Before the cache can be used, the size of the cache has to be set. Depending on the chosen caching policy, `AppManager` will call the appropriate function in the `CacheStore` class.
+
+```java
+
+@Slf4j
+public final class AppManager {
+
+ private static CachingPolicy cachingPolicy;
+ private final DbManager dbManager;
+ private final CacheStore cacheStore;
+
+ private AppManager() {
+ }
+
+ public void initDb() { /* ... */ }
+
+ public static void initCachingPolicy(CachingPolicy policy) { /* ... */ }
+
+ public static void initCacheCapacity(int capacity) { /* ... */ }
+
+ public UserAccount find(final String userId) {
+ LOGGER.info("Trying to find {} in cache", userId);
+ if (cachingPolicy == CachingPolicy.THROUGH
+ || cachingPolicy == CachingPolicy.AROUND) {
+ return cacheStore.readThrough(userId);
+ } else if (cachingPolicy == CachingPolicy.BEHIND) {
+ return cacheStore.readThroughWithWriteBackPolicy(userId);
+ } else if (cachingPolicy == CachingPolicy.ASIDE) {
+ return findAside(userId);
+ }
+ return null;
+ }
+
+ public void save(final UserAccount userAccount) {
+ LOGGER.info("Save record!");
+ if (cachingPolicy == CachingPolicy.THROUGH) {
+ cacheStore.writeThrough(userAccount);
+ } else if (cachingPolicy == CachingPolicy.AROUND) {
+ cacheStore.writeAround(userAccount);
+ } else if (cachingPolicy == CachingPolicy.BEHIND) {
+ cacheStore.writeBehind(userAccount);
+ } else if (cachingPolicy == CachingPolicy.ASIDE) {
+ saveAside(userAccount);
+ }
+ }
+
+ public static String printCacheContent() {
+ return CacheStore.print();
+ }
+
+ // Other properties and methods...
+}
+```
+
+Here is what we do in the main class of the application.
+
+```java
+
+@Slf4j
+public class App {
+
+ public static void main(final String[] args) {
+ boolean isDbMongo = isDbMongo(args);
+ if (isDbMongo) {
+ LOGGER.info("Using the Mongo database engine to run the application.");
+ } else {
+ LOGGER.info("Using the 'in Memory' database to run the application.");
+ }
+ App app = new App(isDbMongo);
+ app.useReadAndWriteThroughStrategy();
+ String splitLine = "==============================================";
+ LOGGER.info(splitLine);
+ app.useReadThroughAndWriteAroundStrategy();
+ LOGGER.info(splitLine);
+ app.useReadThroughAndWriteBehindStrategy();
+ LOGGER.info(splitLine);
+ app.useCacheAsideStategy();
+ LOGGER.info(splitLine);
+ }
+
+ public void useReadAndWriteThroughStrategy() {
+ LOGGER.info("# CachingPolicy.THROUGH");
+ appManager.initCachingPolicy(CachingPolicy.THROUGH);
+
+ var userAccount1 = new UserAccount("001", "John", "He is a boy.");
+
+ appManager.save(userAccount1);
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("001");
+ appManager.find("001");
+ }
+
+ public void useReadThroughAndWriteAroundStrategy() { /* ... */ }
+
+ public void useReadThroughAndWriteBehindStrategy() { /* ... */ }
+
+ public void useCacheAsideStrategy() { /* ... */ }
+}
+```
+
+The program output:
+
+```
+17:00:56.302 [main] INFO com.iluwatar.caching.App -- Using the 'in Memory' database to run the application.
+17:00:56.304 [main] INFO com.iluwatar.caching.App -- # CachingPolicy.THROUGH
+17:00:56.305 [main] INFO com.iluwatar.caching.AppManager -- Save record!
+17:00:56.308 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+UserAccount(userId=001, userName=John, additionalInfo=He is a boy.)
+----
+17:00:56.308 [main] INFO com.iluwatar.caching.AppManager -- Trying to find 001 in cache
+17:00:56.309 [main] INFO com.iluwatar.caching.CacheStore -- # Found in Cache!
+17:00:56.309 [main] INFO com.iluwatar.caching.AppManager -- Trying to find 001 in cache
+17:00:56.309 [main] INFO com.iluwatar.caching.CacheStore -- # Found in Cache!
+17:00:56.309 [main] INFO com.iluwatar.caching.App -- ==============================================
+17:00:56.309 [main] INFO com.iluwatar.caching.App -- # CachingPolicy.AROUND
+17:00:56.309 [main] INFO com.iluwatar.caching.AppManager -- Save record!
+17:00:56.309 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+----
+17:00:56.309 [main] INFO com.iluwatar.caching.AppManager -- Trying to find 002 in cache
+17:00:56.309 [main] INFO com.iluwatar.caching.CacheStore -- # Not found in cache! Go to DB!!
+17:00:56.309 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+UserAccount(userId=002, userName=Jane, additionalInfo=She is a girl.)
+----
+17:00:56.309 [main] INFO com.iluwatar.caching.AppManager -- Trying to find 002 in cache
+17:00:56.309 [main] INFO com.iluwatar.caching.CacheStore -- # Found in Cache!
+17:00:56.309 [main] INFO com.iluwatar.caching.AppManager -- Save record!
+17:00:56.309 [main] INFO com.iluwatar.caching.LruCache -- # 002 has been updated! Removing older version from cache...
+17:00:56.309 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+----
+17:00:56.309 [main] INFO com.iluwatar.caching.AppManager -- Trying to find 002 in cache
+17:00:56.309 [main] INFO com.iluwatar.caching.CacheStore -- # Not found in cache! Go to DB!!
+17:00:56.309 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+UserAccount(userId=002, userName=Jane G., additionalInfo=She is a girl.)
+----
+17:00:56.309 [main] INFO com.iluwatar.caching.AppManager -- Trying to find 002 in cache
+17:00:56.309 [main] INFO com.iluwatar.caching.CacheStore -- # Found in Cache!
+17:00:56.309 [main] INFO com.iluwatar.caching.App -- ==============================================
+17:00:56.309 [main] INFO com.iluwatar.caching.App -- # CachingPolicy.BEHIND
+17:00:56.309 [main] INFO com.iluwatar.caching.AppManager -- Save record!
+17:00:56.309 [main] INFO com.iluwatar.caching.AppManager -- Save record!
+17:00:56.310 [main] INFO com.iluwatar.caching.AppManager -- Save record!
+17:00:56.310 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+UserAccount(userId=005, userName=Isaac, additionalInfo=He is allergic to mustard.)
+UserAccount(userId=004, userName=Rita, additionalInfo=She hates cats.)
+UserAccount(userId=003, userName=Adam, additionalInfo=He likes food.)
+----
+17:00:56.310 [main] INFO com.iluwatar.caching.AppManager -- Trying to find 003 in cache
+17:00:56.310 [main] INFO com.iluwatar.caching.CacheStore -- # Found in cache!
+17:00:56.310 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+UserAccount(userId=003, userName=Adam, additionalInfo=He likes food.)
+UserAccount(userId=005, userName=Isaac, additionalInfo=He is allergic to mustard.)
+UserAccount(userId=004, userName=Rita, additionalInfo=She hates cats.)
+----
+17:00:56.310 [main] INFO com.iluwatar.caching.AppManager -- Save record!
+17:00:56.310 [main] INFO com.iluwatar.caching.CacheStore -- # Cache is FULL! Writing LRU data to DB...
+17:00:56.310 [main] INFO com.iluwatar.caching.LruCache -- # Cache is FULL! Removing 004 from cache...
+17:00:56.310 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+UserAccount(userId=006, userName=Yasha, additionalInfo=She is an only child.)
+UserAccount(userId=003, userName=Adam, additionalInfo=He likes food.)
+UserAccount(userId=005, userName=Isaac, additionalInfo=He is allergic to mustard.)
+----
+17:00:56.310 [main] INFO com.iluwatar.caching.AppManager -- Trying to find 004 in cache
+17:00:56.310 [main] INFO com.iluwatar.caching.CacheStore -- # Not found in Cache!
+17:00:56.310 [main] INFO com.iluwatar.caching.CacheStore -- # Cache is FULL! Writing LRU data to DB...
+17:00:56.310 [main] INFO com.iluwatar.caching.LruCache -- # Cache is FULL! Removing 005 from cache...
+17:00:56.310 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+UserAccount(userId=004, userName=Rita, additionalInfo=She hates cats.)
+UserAccount(userId=006, userName=Yasha, additionalInfo=She is an only child.)
+UserAccount(userId=003, userName=Adam, additionalInfo=He likes food.)
+----
+17:00:56.310 [main] INFO com.iluwatar.caching.App -- ==============================================
+17:00:56.310 [main] INFO com.iluwatar.caching.App -- # CachingPolicy.ASIDE
+17:00:56.310 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+----
+17:00:56.310 [main] INFO com.iluwatar.caching.AppManager -- Save record!
+17:00:56.310 [main] INFO com.iluwatar.caching.AppManager -- Save record!
+17:00:56.310 [main] INFO com.iluwatar.caching.AppManager -- Save record!
+17:00:56.310 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+----
+17:00:56.310 [main] INFO com.iluwatar.caching.AppManager -- Trying to find 003 in cache
+17:00:56.313 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+UserAccount(userId=003, userName=Adam, additionalInfo=He likes food.)
+----
+17:00:56.313 [main] INFO com.iluwatar.caching.AppManager -- Trying to find 004 in cache
+17:00:56.313 [main] INFO com.iluwatar.caching.App --
+--CACHE CONTENT--
+UserAccount(userId=004, userName=Rita, additionalInfo=She hates cats.)
+UserAccount(userId=003, userName=Adam, additionalInfo=He likes food.)
+----
+17:00:56.313 [main] INFO com.iluwatar.caching.App -- ==============================================
+17:00:56.314 [Thread-0] INFO com.iluwatar.caching.CacheStore -- # flushCache...
+```
+
+Implementing the Java Caching Design Pattern using various strategies like LRU cache and write-through caching significantly enhances application performance and scalability.
+
+## When to Use the Caching Pattern in Java
+
+Use the Caching pattern when
+
+* Repetitious acquisition, initialization, and release of the same resource cause unnecessary performance overhead
+* In scenarios where the cost of recomputing or re-fetching data is significantly higher than storing and retrieving it from cache
+* For read-heavy applications with relatively static data or data that changes infrequently
+
+## Real-World Applications of Caching Pattern in Java
+
+* Web page caching to reduce server load and improve response time
+* Database query caching to avoid repeated expensive SQL queries
+* Caching results of CPU-intensive computations
+* Content Delivery Networks (CDNs) for caching static resources like images, CSS, and JavaScript files closer to the end users
+
+## Benefits and Trade-offs of Caching Pattern
+
+Benefits:
+
+* Improved Performance: Significantly reduces data access latency, leading to faster application performance
+* Reduced Load: Decreases the load on the underlying data source, which can lead to cost savings and increased longevity of the resource
+* Scalability: Enhances the scalability of applications by efficiently handling increases in load without proportional increases in resource utilization
+
+Trade-Offs:
+
+* Complexity: Introduces complexity in terms of cache invalidation, consistency, and synchronization
+* Resource Utilization: Requires additional memory or storage resources to maintain the cache
+* Stale Data: There's a risk of serving outdated data if the cache is not properly invalidated or updated when the underlying data changes
+
+## Related Java Design Patterns
+
+* [Proxy](https://java-design-patterns.com/patterns/proxy/): Caching can be implemented using the Proxy pattern, where the proxy object intercepts requests and returns cached data if available
+* [Observer](https://java-design-patterns.com/patterns/observer/): Can be used to notify the cache when the underlying data changes, so that it can be updated or invalidated accordingly
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): Can be used to add caching behavior to an existing object without modifying its code
+* [Strategy](https://java-design-patterns.com/patterns/strategy/): Different caching strategies can be implemented using the Strategy pattern, allowing the application to switch between them at runtime
+
+## References and Credits
+
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [High Performance Browser Networking](https://amzn.to/3TiNNY4)
+* [Java EE 8 High Performance](https://amzn.to/44T8vmH)
+* [Java Performance: In-Depth Advice for Tuning and Programming Java 8, 11, and Beyond](https://amzn.to/3yyD58W)
+* [Java Performance: The Definitive Guide: Getting the Most Out of Your Code](https://amzn.to/3Wu5neF)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3PMAHRZ)
+* [Scalable Internet Architectures](https://amzn.to/48V3ni9)
+* [Write-through, write-around, write-back: Cache explained (ComputerWeekly)](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained)
+* [Cache-Aside Pattern (Microsoft)](https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside)
diff --git a/caching/docker-compose.yml b/caching/docker-compose.yml
new file mode 100644
index 000000000000..9643d91cf3b7
--- /dev/null
+++ b/caching/docker-compose.yml
@@ -0,0 +1,36 @@
+#
+# This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+#
+# The MIT License
+# Copyright © 2014-2022 Ilkka Seppälä
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+version: '3.7'
+services:
+ mongodb_container:
+ image: mongo:latest
+ environment:
+ MONGO_INITDB_ROOT_USERNAME: root
+ MONGO_INITDB_ROOT_PASSWORD: rootpassword
+ ports:
+ - '27017:27017'
+ volumes:
+ - ./mongo-data/:/data/db
diff --git a/caching/etc/caching-flowchart.png b/caching/etc/caching-flowchart.png
new file mode 100644
index 000000000000..8068392cd9bd
Binary files /dev/null and b/caching/etc/caching-flowchart.png differ
diff --git a/caching/etc/caching.png b/caching/etc/caching.png
new file mode 100644
index 000000000000..5be2dc0ea7f8
Binary files /dev/null and b/caching/etc/caching.png differ
diff --git a/caching/etc/caching.ucls b/caching/etc/caching.ucls
new file mode 100644
index 000000000000..a058277ea2fd
--- /dev/null
+++ b/caching/etc/caching.ucls
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/caching/etc/caching.urm.puml b/caching/etc/caching.urm.puml
new file mode 100644
index 000000000000..f6f2e4732005
--- /dev/null
+++ b/caching/etc/caching.urm.puml
@@ -0,0 +1,119 @@
+@startuml
+package com.iluwatar.caching.constants {
+ class CachingConstants {
+ + ADD_INFO : String {static}
+ + USER_ACCOUNT : String {static}
+ + USER_ID : String {static}
+ + USER_NAME : String {static}
+ + CachingConstants()
+ }
+}
+package com.iluwatar.caching {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ + main(args : String[]) {static}
+ + useCacheAsideStrategy()
+ + useReadAndWriteThroughStrategy()
+ + useReadThroughAndWriteAroundStrategy()
+ + useReadThroughAndWriteBehindStrategy()
+ }
+ class AppManager {
+ - cachingPolicy : CachingPolicy {static}
+ - AppManager()
+ + find(userId : String) : UserAccount {static}
+ - findAside(userId : String) : UserAccount {static}
+ + initCacheCapacity(capacity : int) {static}
+ + initCachingPolicy(policy : CachingPolicy) {static}
+ + initDb(useMongoDb : boolean) {static}
+ + printCacheContent() : String {static}
+ + save(userAccount : UserAccount) {static}
+ - saveAside(userAccount : UserAccount) {static}
+ }
+ class CacheStore {
+ - LOGGER : Logger {static}
+ - cache : LruCache {static}
+ - CacheStore()
+ + clearCache() {static}
+ + flushCache() {static}
+ + get(userId : String) : UserAccount {static}
+ + initCapacity(capacity : int) {static}
+ + invalidate(userId : String) {static}
+ + print() : String {static}
+ + readThrough(userId : String) : UserAccount {static}
+ + readThroughWithWriteBackPolicy(userId : String) : UserAccount {static}
+ + set(userId : String, userAccount : UserAccount) {static}
+ + writeAround(userAccount : UserAccount) {static}
+ + writeBehind(userAccount : UserAccount) {static}
+ + writeThrough(userAccount : UserAccount) {static}
+ }
+ enum CachingPolicy {
+ + AROUND {static}
+ + ASIDE {static}
+ + BEHIND {static}
+ + THROUGH {static}
+ - policy : String
+ + getPolicy() : String
+ + valueOf(name : String) : CachingPolicy {static}
+ + values() : CachingPolicy[] {static}
+ }
+ class DbManager {
+ - db : MongoDatabase {static}
+ - mongoClient : MongoClient {static}
+ - useMongoDB : boolean {static}
+ - virtualDB : Map {static}
+ - DbManager()
+ + connect() {static}
+ + createVirtualDb() {static}
+ + readFromDb(userId : String) : UserAccount {static}
+ + updateDb(userAccount : UserAccount) {static}
+ + upsertDb(userAccount : UserAccount) {static}
+ + writeToDb(userAccount : UserAccount) {static}
+ }
+ class LruCache {
+ - LOGGER : Logger {static}
+ ~ cache : Map
+ ~ capacity : int
+ ~ end : Node
+ ~ head : Node
+ + LruCache(capacity : int)
+ + clear()
+ + contains(userId : String) : boolean
+ + get(userId : String) : UserAccount
+ + getCacheDataInListForm() : List
+ + getLruData() : UserAccount
+ + invalidate(userId : String)
+ + isFull() : boolean
+ + remove(node : Node)
+ + set(userId : String, userAccount : UserAccount)
+ + setCapacity(newCapacity : int)
+ + setHead(node : Node)
+ }
+ ~class Node {
+ ~ next : Node
+ ~ previous : Node
+ ~ userAccount : UserAccount
+ ~ userId : String
+ + Node(this$0 : String, userId : UserAccount)
+ }
+ class UserAccount {
+ - additionalInfo : String
+ - userId : String
+ - userName : String
+ + UserAccount(userId : String, userName : String, additionalInfo : String)
+ + getAdditionalInfo() : String
+ + getUserId() : String
+ + getUserName() : String
+ + setAdditionalInfo(additionalInfo : String)
+ + setUserId(userId : String)
+ + setUserName(userName : String)
+ + toString() : String
+ }
+}
+Node --+ LruCache
+LruCache --> "-head" Node
+Node --> "-previous" Node
+AppManager --> "-cachingPolicy" CachingPolicy
+Node --> "-userAccount" UserAccount
+CacheStore --> "-cache" LruCache
+@enduml
diff --git a/caching/pom.xml b/caching/pom.xml
new file mode 100644
index 000000000000..3ffce74af493
--- /dev/null
+++ b/caching/pom.xml
@@ -0,0 +1,96 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ caching
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mongodb
+ bson
+
+
+ org.mongodb
+ mongodb-driver-legacy
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.caching.App
+
+
+
+
+
+
+
+
+
diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java
new file mode 100644
index 000000000000..8d6af6c090d8
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/App.java
@@ -0,0 +1,211 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching;
+
+import com.iluwatar.caching.database.DbManager;
+import com.iluwatar.caching.database.DbManagerFactory;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing
+ * the resources immediately after their use. The resources retain their identity, are kept in some
+ * fast-access storage, and are re-used to avoid having to acquire them again. There are four main
+ * caching strategies/techniques in this pattern; each with their own pros and cons. They are
+ * write-through
which writes data to the cache and DB in a single transaction,
+ * write-around
which writes data immediately into the DB instead of the cache,
+ * write-behind
which writes data into the cache initially whilst the data is only written
+ * into the DB when the cache is full, and cache-aside
which pushes the responsibility
+ * of keeping the data synchronized in both data sources to the application itself. The
+ * read-through
strategy is also included in the mentioned four strategies -- returns data
+ * from the cache to the caller if it exists else queries from DB and stores it into
+ * the cache for future use. These strategies determine when the data in the cache should be written
+ * back to the backing store (i.e. Database) and help keep both data sources
+ * synchronized/up-to-date. This pattern can improve performance and also helps to
+ * maintainconsistency between data held in the cache and the data in the underlying data store.
+ *
+ * In this example, the user account ({@link UserAccount}) entity is used as the underlying
+ * application data. The cache itself is implemented as an internal (Java) data structure. It adopts
+ * a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The four
+ * strategies are individually tested. The testing of the cache is restricted towards saving and
+ * querying of user accounts from the underlying data store( {@link DbManager}). The main class (
+ * {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
+ * whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
+ * ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
+ * (depending on the preferred caching policy/strategy).
+ *
+ *
{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager}
+ *
+ *
There are 2 ways to launch the application. - to use "in Memory" database. - to use the
+ * MongoDb as a database
+ *
+ *
To run the application with "in Memory" database, just launch it without parameters Example:
+ * 'java -jar app.jar'
+ *
+ *
To run the application with MongoDb you need to be installed the MongoDb in your system, or to
+ * launch it in the docker container. You may launch docker container from the root of current
+ * module with command: 'docker-compose up' Then you can start the application with parameter
+ * --mongo Example: 'java -jar app.jar --mongo'
+ *
+ * @see CacheStore
+ * @see LruCache
+ * @see CachingPolicy
+ */
+@Slf4j
+public class App {
+ /** Constant parameter name to use mongoDB. */
+ private static final String USE_MONGO_DB = "--mongo";
+
+ /** Application manager. */
+ private final AppManager appManager;
+
+ /**
+ * Constructor of current App.
+ *
+ * @param isMongo boolean
+ */
+ public App(final boolean isMongo) {
+ DbManager dbManager = DbManagerFactory.initDb(isMongo);
+ appManager = new AppManager(dbManager);
+ appManager.initDb();
+ }
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(final String[] args) {
+ // VirtualDB (instead of MongoDB) was used in running the JUnit tests
+ // and the App class to avoid Maven compilation errors. Set flag to
+ // true to run the tests with MongoDB (provided that MongoDB is
+ // installed and socket connection is open).
+ boolean isDbMongo = isDbMongo(args);
+ if (isDbMongo) {
+ LOGGER.info("Using the Mongo database engine to run the application.");
+ } else {
+ LOGGER.info("Using the 'in Memory' database to run the application.");
+ }
+ App app = new App(isDbMongo);
+ app.useReadAndWriteThroughStrategy();
+ String splitLine = "==============================================";
+ LOGGER.info(splitLine);
+ app.useReadThroughAndWriteAroundStrategy();
+ LOGGER.info(splitLine);
+ app.useReadThroughAndWriteBehindStrategy();
+ LOGGER.info(splitLine);
+ app.useCacheAsideStrategy();
+ LOGGER.info(splitLine);
+ }
+
+ /**
+ * Check the input parameters. if
+ *
+ * @param args input params
+ * @return true if there is "--mongo" parameter in arguments
+ */
+ private static boolean isDbMongo(final String[] args) {
+ for (String arg : args) {
+ if (arg.equals(USE_MONGO_DB)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Read-through and write-through. */
+ public void useReadAndWriteThroughStrategy() {
+ LOGGER.info("# CachingPolicy.THROUGH");
+ appManager.initCachingPolicy(CachingPolicy.THROUGH);
+
+ var userAccount1 = new UserAccount("001", "John", "He is a boy.");
+
+ appManager.save(userAccount1);
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("001");
+ appManager.find("001");
+ }
+
+ /** Read-through and write-around. */
+ public void useReadThroughAndWriteAroundStrategy() {
+ LOGGER.info("# CachingPolicy.AROUND");
+ appManager.initCachingPolicy(CachingPolicy.AROUND);
+
+ var userAccount2 = new UserAccount("002", "Jane", "She is a girl.");
+
+ appManager.save(userAccount2);
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("002");
+ LOGGER.info(appManager.printCacheContent());
+ userAccount2 = appManager.find("002");
+ userAccount2.setUserName("Jane G.");
+ appManager.save(userAccount2);
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("002");
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("002");
+ }
+
+ /** Read-through and write-behind. */
+ public void useReadThroughAndWriteBehindStrategy() {
+ LOGGER.info("# CachingPolicy.BEHIND");
+ appManager.initCachingPolicy(CachingPolicy.BEHIND);
+
+ var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
+ var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
+ var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
+
+ appManager.save(userAccount3);
+ appManager.save(userAccount4);
+ appManager.save(userAccount5);
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("003");
+ LOGGER.info(appManager.printCacheContent());
+ UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
+ appManager.save(userAccount6);
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("004");
+ LOGGER.info(appManager.printCacheContent());
+ }
+
+ /** Cache-Aside. */
+ public void useCacheAsideStrategy() {
+ LOGGER.info("# CachingPolicy.ASIDE");
+ appManager.initCachingPolicy(CachingPolicy.ASIDE);
+ LOGGER.info(appManager.printCacheContent());
+
+ var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
+ var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
+ var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
+ appManager.save(userAccount3);
+ appManager.save(userAccount4);
+ appManager.save(userAccount5);
+
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("003");
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("004");
+ LOGGER.info(appManager.printCacheContent());
+ }
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java
new file mode 100644
index 000000000000..c1d21fea33fe
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java
@@ -0,0 +1,152 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching;
+
+import com.iluwatar.caching.database.DbManager;
+import java.util.Optional;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * AppManager helps to bridge the gap in communication between the main class and the application's
+ * back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
+ * also initialized here. Before the cache can be used, the size of the cache has to be set.
+ * Depending on the chosen caching policy, AppManager will call the appropriate function in the
+ * CacheStore class.
+ */
+@Slf4j
+public class AppManager {
+ /** Caching Policy. */
+ private CachingPolicy cachingPolicy;
+
+ /** Database Manager. */
+ private final DbManager dbManager;
+
+ /** Cache Store. */
+ private final CacheStore cacheStore;
+
+ /**
+ * Constructor.
+ *
+ * @param newDbManager database manager
+ */
+ public AppManager(final DbManager newDbManager) {
+ this.dbManager = newDbManager;
+ this.cacheStore = new CacheStore(newDbManager);
+ }
+
+ /**
+ * Developer/Tester is able to choose whether the application should use MongoDB as its underlying
+ * data storage or a simple Java data structure to (temporarily) store the data/objects during
+ * runtime.
+ */
+ public void initDb() {
+ dbManager.connect();
+ }
+
+ /**
+ * Initialize caching policy.
+ *
+ * @param policy is a {@link CachingPolicy}
+ */
+ public void initCachingPolicy(final CachingPolicy policy) {
+ cachingPolicy = policy;
+ if (cachingPolicy == CachingPolicy.BEHIND) {
+ Runtime.getRuntime().addShutdownHook(new Thread(cacheStore::flushCache));
+ }
+ cacheStore.clearCache();
+ }
+
+ /**
+ * Find user account.
+ *
+ * @param userId String
+ * @return {@link UserAccount}
+ */
+ public UserAccount find(final String userId) {
+ LOGGER.info("Trying to find {} in cache", userId);
+ if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
+ return cacheStore.readThrough(userId);
+ } else if (cachingPolicy == CachingPolicy.BEHIND) {
+ return cacheStore.readThroughWithWriteBackPolicy(userId);
+ } else if (cachingPolicy == CachingPolicy.ASIDE) {
+ return findAside(userId);
+ }
+ return null;
+ }
+
+ /**
+ * Save user account.
+ *
+ * @param userAccount {@link UserAccount}
+ */
+ public void save(final UserAccount userAccount) {
+ LOGGER.info("Save record!");
+ if (cachingPolicy == CachingPolicy.THROUGH) {
+ cacheStore.writeThrough(userAccount);
+ } else if (cachingPolicy == CachingPolicy.AROUND) {
+ cacheStore.writeAround(userAccount);
+ } else if (cachingPolicy == CachingPolicy.BEHIND) {
+ cacheStore.writeBehind(userAccount);
+ } else if (cachingPolicy == CachingPolicy.ASIDE) {
+ saveAside(userAccount);
+ }
+ }
+
+ /**
+ * Returns String.
+ *
+ * @return String
+ */
+ public String printCacheContent() {
+ return cacheStore.print();
+ }
+
+ /**
+ * Cache-Aside save user account helper.
+ *
+ * @param userAccount {@link UserAccount}
+ */
+ private void saveAside(final UserAccount userAccount) {
+ dbManager.updateDb(userAccount);
+ cacheStore.invalidate(userAccount.getUserId());
+ }
+
+ /**
+ * Cache-Aside find user account helper.
+ *
+ * @param userId String
+ * @return {@link UserAccount}
+ */
+ private UserAccount findAside(final String userId) {
+ return Optional.ofNullable(cacheStore.get(userId))
+ .or(
+ () -> {
+ Optional userAccount = Optional.ofNullable(dbManager.readFromDb(userId));
+ userAccount.ifPresent(account -> cacheStore.set(userId, account));
+ return userAccount;
+ })
+ .orElse(null);
+ }
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java
new file mode 100644
index 000000000000..b26c52b22159
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java
@@ -0,0 +1,210 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching;
+
+import com.iluwatar.caching.database.DbManager;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import lombok.extern.slf4j.Slf4j;
+
+/** The caching strategies are implemented in this class. */
+@Slf4j
+public class CacheStore {
+ /** Cache capacity. */
+ private static final int CAPACITY = 3;
+
+ /** Lru cache see {@link LruCache}. */
+ private LruCache cache;
+
+ /** DbManager. */
+ private final DbManager dbManager;
+
+ /**
+ * Cache Store.
+ *
+ * @param dataBaseManager {@link DbManager}
+ */
+ public CacheStore(final DbManager dataBaseManager) {
+ this.dbManager = dataBaseManager;
+ initCapacity(CAPACITY);
+ }
+
+ /**
+ * Init cache capacity.
+ *
+ * @param capacity int
+ */
+ public void initCapacity(final int capacity) {
+ if (cache == null) {
+ cache = new LruCache(capacity);
+ } else {
+ cache.setCapacity(capacity);
+ }
+ }
+
+ /**
+ * Get user account using read-through cache.
+ *
+ * @param userId {@link String}
+ * @return {@link UserAccount}
+ */
+ public UserAccount readThrough(final String userId) {
+ if (cache.contains(userId)) {
+ LOGGER.info("# Found in Cache!");
+ return cache.get(userId);
+ }
+ LOGGER.info("# Not found in cache! Go to DB!!");
+ UserAccount userAccount = dbManager.readFromDb(userId);
+ cache.set(userId, userAccount);
+ return userAccount;
+ }
+
+ /**
+ * Get user account using write-through cache.
+ *
+ * @param userAccount {@link UserAccount}
+ */
+ public void writeThrough(final UserAccount userAccount) {
+ if (cache.contains(userAccount.getUserId())) {
+ dbManager.updateDb(userAccount);
+ } else {
+ dbManager.writeToDb(userAccount);
+ }
+ cache.set(userAccount.getUserId(), userAccount);
+ }
+
+ /**
+ * Get user account using write-around cache.
+ *
+ * @param userAccount {@link UserAccount}
+ */
+ public void writeAround(final UserAccount userAccount) {
+ if (cache.contains(userAccount.getUserId())) {
+ dbManager.updateDb(userAccount);
+ // Cache data has been updated -- remove older
+ cache.invalidate(userAccount.getUserId());
+ // version from cache.
+ } else {
+ dbManager.writeToDb(userAccount);
+ }
+ }
+
+ /**
+ * Get user account using read-through cache with write-back policy.
+ *
+ * @param userId {@link String}
+ * @return {@link UserAccount}
+ */
+ public UserAccount readThroughWithWriteBackPolicy(final String userId) {
+ if (cache.contains(userId)) {
+ LOGGER.info("# Found in cache!");
+ return cache.get(userId);
+ }
+ LOGGER.info("# Not found in Cache!");
+ UserAccount userAccount = dbManager.readFromDb(userId);
+ if (cache.isFull()) {
+ LOGGER.info("# Cache is FULL! Writing LRU data to DB...");
+ UserAccount toBeWrittenToDb = cache.getLruData();
+ dbManager.upsertDb(toBeWrittenToDb);
+ }
+ cache.set(userId, userAccount);
+ return userAccount;
+ }
+
+ /**
+ * Set user account.
+ *
+ * @param userAccount {@link UserAccount}
+ */
+ public void writeBehind(final UserAccount userAccount) {
+ if (cache.isFull() && !cache.contains(userAccount.getUserId())) {
+ LOGGER.info("# Cache is FULL! Writing LRU data to DB...");
+ UserAccount toBeWrittenToDb = cache.getLruData();
+ dbManager.upsertDb(toBeWrittenToDb);
+ }
+ cache.set(userAccount.getUserId(), userAccount);
+ }
+
+ /** Clears cache. */
+ public void clearCache() {
+ if (cache != null) {
+ cache.clear();
+ }
+ }
+
+ /** Writes remaining content in the cache into the DB. */
+ public void flushCache() {
+ LOGGER.info("# flushCache...");
+ Optional.ofNullable(cache)
+ .map(LruCache::getCacheDataInListForm)
+ .orElse(List.of())
+ .forEach(dbManager::updateDb);
+ dbManager.disconnect();
+ }
+
+ /**
+ * Print user accounts.
+ *
+ * @return {@link String}
+ */
+ public String print() {
+ return Optional.ofNullable(cache)
+ .map(LruCache::getCacheDataInListForm)
+ .orElse(List.of())
+ .stream()
+ .map(userAccount -> userAccount.toString() + "\n")
+ .collect(Collectors.joining("", "\n--CACHE CONTENT--\n", "----"));
+ }
+
+ /**
+ * Delegate to backing cache store.
+ *
+ * @param userId {@link String}
+ * @return {@link UserAccount}
+ */
+ public UserAccount get(final String userId) {
+ return cache.get(userId);
+ }
+
+ /**
+ * Delegate to backing cache store.
+ *
+ * @param userId {@link String}
+ * @param userAccount {@link UserAccount}
+ */
+ public void set(final String userId, final UserAccount userAccount) {
+ cache.set(userId, userAccount);
+ }
+
+ /**
+ * Delegate to backing cache store.
+ *
+ * @param userId {@link String}
+ */
+ public void invalidate(final String userId) {
+ cache.invalidate(userId);
+ }
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
new file mode 100644
index 000000000000..0ec07ced7b8f
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
@@ -0,0 +1,45 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/** Enum class containing the four caching strategies implemented in the pattern. */
+@AllArgsConstructor
+@Getter
+public enum CachingPolicy {
+ /** Through. */
+ THROUGH("through"),
+ /** AROUND. */
+ AROUND("around"),
+ /** BEHIND. */
+ BEHIND("behind"),
+ /** ASIDE. */
+ ASIDE("aside");
+
+ /** Policy value. */
+ private final String policy;
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java
new file mode 100644
index 000000000000..9c9107de6f88
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java
@@ -0,0 +1,244 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Data structure/implementation of the application's cache. The data structure consists of a hash
+ * table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the
+ * LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated,
+ * the data is moved to the front of the list to depict itself as the most-recently-used data. The
+ * LRU data is always at the end of the list.
+ */
+@Slf4j
+public class LruCache {
+ /** Static class Node. */
+ static class Node {
+ /** user id. */
+ private final String userId;
+
+ /** User Account. */
+ private UserAccount userAccount;
+
+ /** previous. */
+ private Node previous;
+
+ /** next. */
+ private Node next;
+
+ /**
+ * Node definition.
+ *
+ * @param id String
+ * @param account {@link UserAccount}
+ */
+ Node(final String id, final UserAccount account) {
+ this.userId = id;
+ this.userAccount = account;
+ }
+ }
+
+ /** Capacity of Cache. */
+ private int capacity;
+
+ /** Cache {@link HashMap}. */
+ private Map cache = new HashMap<>();
+
+ /** Head. */
+ private Node head;
+
+ /** End. */
+ private Node end;
+
+ /**
+ * Constructor.
+ *
+ * @param cap Integer.
+ */
+ public LruCache(final int cap) {
+ this.capacity = cap;
+ }
+
+ /**
+ * Get user account.
+ *
+ * @param userId String
+ * @return {@link UserAccount}
+ */
+ public UserAccount get(final String userId) {
+ if (cache.containsKey(userId)) {
+ var node = cache.get(userId);
+ remove(node);
+ setHead(node);
+ return node.userAccount;
+ }
+ return null;
+ }
+
+ /**
+ * Remove node from linked list.
+ *
+ * @param node {@link Node}
+ */
+ public void remove(final Node node) {
+ if (node.previous != null) {
+ node.previous.next = node.next;
+ } else {
+ head = node.next;
+ }
+ if (node.next != null) {
+ node.next.previous = node.previous;
+ } else {
+ end = node.previous;
+ }
+ }
+
+ /**
+ * Move node to the front of the list.
+ *
+ * @param node {@link Node}
+ */
+ public void setHead(final Node node) {
+ node.next = head;
+ node.previous = null;
+ if (head != null) {
+ head.previous = node;
+ }
+ head = node;
+ if (end == null) {
+ end = head;
+ }
+ }
+
+ /**
+ * Set user account.
+ *
+ * @param userAccount {@link UserAccount}
+ * @param userId {@link String}
+ */
+ public void set(final String userId, final UserAccount userAccount) {
+ if (cache.containsKey(userId)) {
+ var old = cache.get(userId);
+ old.userAccount = userAccount;
+ remove(old);
+ setHead(old);
+ } else {
+ var newNode = new Node(userId, userAccount);
+ if (cache.size() >= capacity) {
+ LOGGER.info("# Cache is FULL! Removing {} from cache...", end.userId);
+ cache.remove(end.userId); // remove LRU data from cache.
+ remove(end);
+ setHead(newNode);
+ } else {
+ setHead(newNode);
+ }
+ cache.put(userId, newNode);
+ }
+ }
+
+ /**
+ * Check if Cache contains the userId.
+ *
+ * @param userId {@link String}
+ * @return boolean
+ */
+ public boolean contains(final String userId) {
+ return cache.containsKey(userId);
+ }
+
+ /**
+ * Invalidate cache for user.
+ *
+ * @param userId {@link String}
+ */
+ public void invalidate(final String userId) {
+ var toBeRemoved = cache.remove(userId);
+ if (toBeRemoved != null) {
+ LOGGER.info("# {} has been updated! " + "Removing older version from cache...", userId);
+ remove(toBeRemoved);
+ }
+ }
+
+ /**
+ * Check if the cache is full.
+ *
+ * @return boolean
+ */
+ public boolean isFull() {
+ return cache.size() >= capacity;
+ }
+
+ /**
+ * Get LRU data.
+ *
+ * @return {@link UserAccount}
+ */
+ public UserAccount getLruData() {
+ return end.userAccount;
+ }
+
+ /** Clear cache. */
+ public void clear() {
+ head = null;
+ end = null;
+ cache.clear();
+ }
+
+ /**
+ * Returns cache data in list form.
+ *
+ * @return {@link List}
+ */
+ public List getCacheDataInListForm() {
+ var listOfCacheData = new ArrayList();
+ var temp = head;
+ while (temp != null) {
+ listOfCacheData.add(temp.userAccount);
+ temp = temp.next;
+ }
+ return listOfCacheData;
+ }
+
+ /**
+ * Set cache capacity.
+ *
+ * @param newCapacity int
+ */
+ public void setCapacity(final int newCapacity) {
+ if (capacity > newCapacity) {
+ // Behavior can be modified to accommodate
+ // for decrease in cache size. For now, we'll
+ clear();
+ // just clear the cache.
+ } else {
+ this.capacity = newCapacity;
+ }
+ }
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/UserAccount.java b/caching/src/main/java/com/iluwatar/caching/UserAccount.java
new file mode 100644
index 000000000000..561c942ac781
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/UserAccount.java
@@ -0,0 +1,46 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/** Entity class (stored in cache and DB) used in the application. */
+@Data
+@AllArgsConstructor
+@ToString
+@EqualsAndHashCode
+public class UserAccount {
+ /** User Id. */
+ private String userId;
+
+ /** User Name. */
+ private String userName;
+
+ /** Additional Info. */
+ private String additionalInfo;
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java b/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java
new file mode 100644
index 000000000000..5e8fc415df4c
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java
@@ -0,0 +1,43 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching.constants;
+
+/** Constant class for defining constants. */
+public final class CachingConstants {
+ /** User Account. */
+ public static final String USER_ACCOUNT = "user_accounts";
+
+ /** User ID. */
+ public static final String USER_ID = "userID";
+
+ /** User Name. */
+ public static final String USER_NAME = "userName";
+
+ /** Additional Info. */
+ public static final String ADD_INFO = "additionalInfo";
+
+ /** Constructor. */
+ private CachingConstants() {}
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/constants/package-info.java b/caching/src/main/java/com/iluwatar/caching/constants/package-info.java
new file mode 100644
index 000000000000..b94476cbabeb
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/constants/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/** Constants. */
+package com.iluwatar.caching.constants;
diff --git a/caching/src/main/java/com/iluwatar/caching/database/DbManager.java b/caching/src/main/java/com/iluwatar/caching/database/DbManager.java
new file mode 100644
index 000000000000..14b98a66b52b
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/database/DbManager.java
@@ -0,0 +1,72 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching.database;
+
+import com.iluwatar.caching.UserAccount;
+
+/**
+ * DBManager handles the communication with the underlying data store i.e. Database. It contains the
+ * implemented methods for querying, inserting, and updating data. MongoDB was used as the database
+ * for the application.
+ */
+public interface DbManager {
+ /** Connect to DB. */
+ void connect();
+
+ /** Disconnect from DB. */
+ void disconnect();
+
+ /**
+ * Read from DB.
+ *
+ * @param userId {@link String}
+ * @return {@link UserAccount}
+ */
+ UserAccount readFromDb(String userId);
+
+ /**
+ * Write to DB.
+ *
+ * @param userAccount {@link UserAccount}
+ * @return {@link UserAccount}
+ */
+ UserAccount writeToDb(UserAccount userAccount);
+
+ /**
+ * Update record.
+ *
+ * @param userAccount {@link UserAccount}
+ * @return {@link UserAccount}
+ */
+ UserAccount updateDb(UserAccount userAccount);
+
+ /**
+ * Update record or Insert if not exists.
+ *
+ * @param userAccount {@link UserAccount}
+ * @return {@link UserAccount}
+ */
+ UserAccount upsertDb(UserAccount userAccount);
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java b/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java
new file mode 100644
index 000000000000..92031b7c95b0
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java
@@ -0,0 +1,44 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching.database;
+
+/** Creates the database connection according the input parameter. */
+public final class DbManagerFactory {
+ /** Private constructor. */
+ private DbManagerFactory() {}
+
+ /**
+ * Init database.
+ *
+ * @param isMongo boolean
+ * @return {@link DbManager}
+ */
+ public static DbManager initDb(final boolean isMongo) {
+ if (isMongo) {
+ return new MongoDb();
+ }
+ return new VirtualDb();
+ }
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java b/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java
new file mode 100644
index 000000000000..e47eef55cd8c
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java
@@ -0,0 +1,148 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching.database;
+
+import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
+import static com.iluwatar.caching.constants.CachingConstants.USER_ACCOUNT;
+import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
+import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
+
+import com.iluwatar.caching.UserAccount;
+import com.iluwatar.caching.constants.CachingConstants;
+import com.mongodb.MongoClient;
+import com.mongodb.MongoClientOptions;
+import com.mongodb.MongoCredential;
+import com.mongodb.ServerAddress;
+import com.mongodb.client.MongoDatabase;
+import com.mongodb.client.model.UpdateOptions;
+import lombok.extern.slf4j.Slf4j;
+import org.bson.Document;
+
+/** Implementation of DatabaseManager. implements base methods to work with MongoDb. */
+@Slf4j
+public class MongoDb implements DbManager {
+ private static final String DATABASE_NAME = "admin";
+ private static final String MONGO_USER = "root";
+ private static final String MONGO_PASSWORD = "rootpassword";
+ private MongoClient client;
+ private MongoDatabase db;
+
+ void setDb(MongoDatabase db) {
+ this.db = db;
+ }
+
+ /** Connect to Db. Check th connection */
+ @Override
+ public void connect() {
+ MongoCredential mongoCredential =
+ MongoCredential.createCredential(MONGO_USER, DATABASE_NAME, MONGO_PASSWORD.toCharArray());
+ MongoClientOptions options = MongoClientOptions.builder().build();
+ client = new MongoClient(new ServerAddress(), mongoCredential, options);
+ db = client.getDatabase(DATABASE_NAME);
+ }
+
+ @Override
+ public void disconnect() {
+ client.close();
+ }
+
+ /**
+ * Read data from DB.
+ *
+ * @param userId {@link String}
+ * @return {@link UserAccount}
+ */
+ @Override
+ public UserAccount readFromDb(final String userId) {
+ var iterable =
+ db.getCollection(CachingConstants.USER_ACCOUNT).find(new Document(USER_ID, userId));
+ if (iterable.first() == null) {
+ return null;
+ }
+ Document doc = iterable.first();
+ if (doc != null) {
+ String userName = doc.getString(USER_NAME);
+ String appInfo = doc.getString(ADD_INFO);
+ return new UserAccount(userId, userName, appInfo);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Write data to DB.
+ *
+ * @param userAccount {@link UserAccount}
+ * @return {@link UserAccount}
+ */
+ @Override
+ public UserAccount writeToDb(final UserAccount userAccount) {
+ db.getCollection(USER_ACCOUNT)
+ .insertOne(
+ new Document(USER_ID, userAccount.getUserId())
+ .append(USER_NAME, userAccount.getUserName())
+ .append(ADD_INFO, userAccount.getAdditionalInfo()));
+ return userAccount;
+ }
+
+ /**
+ * Update DB.
+ *
+ * @param userAccount {@link UserAccount}
+ * @return {@link UserAccount}
+ */
+ @Override
+ public UserAccount updateDb(final UserAccount userAccount) {
+ Document id = new Document(USER_ID, userAccount.getUserId());
+ Document dataSet =
+ new Document(USER_NAME, userAccount.getUserName())
+ .append(ADD_INFO, userAccount.getAdditionalInfo());
+ db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(id, new Document("$set", dataSet));
+ return userAccount;
+ }
+
+ /**
+ * Update data if exists.
+ *
+ * @param userAccount {@link UserAccount}
+ * @return {@link UserAccount}
+ */
+ @Override
+ public UserAccount upsertDb(final UserAccount userAccount) {
+ String userId = userAccount.getUserId();
+ String userName = userAccount.getUserName();
+ String additionalInfo = userAccount.getAdditionalInfo();
+ db.getCollection(CachingConstants.USER_ACCOUNT)
+ .updateOne(
+ new Document(USER_ID, userId),
+ new Document(
+ "$set",
+ new Document(USER_ID, userId)
+ .append(USER_NAME, userName)
+ .append(ADD_INFO, additionalInfo)),
+ new UpdateOptions().upsert(true));
+ return userAccount;
+ }
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java b/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java
new file mode 100644
index 000000000000..6040ca174a21
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java
@@ -0,0 +1,94 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching.database;
+
+import com.iluwatar.caching.UserAccount;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Implementation of DatabaseManager. implements base methods to work with hashMap as database. */
+public class VirtualDb implements DbManager {
+ /** Virtual DataBase. */
+ private Map db;
+
+ /** Creates new HashMap. */
+ @Override
+ public void connect() {
+ db = new HashMap<>();
+ }
+
+ @Override
+ public void disconnect() {
+ db = null;
+ }
+
+ /**
+ * Read from Db.
+ *
+ * @param userId {@link String}
+ * @return {@link UserAccount}
+ */
+ @Override
+ public UserAccount readFromDb(final String userId) {
+ if (db.containsKey(userId)) {
+ return db.get(userId);
+ }
+ return null;
+ }
+
+ /**
+ * Write to DB.
+ *
+ * @param userAccount {@link UserAccount}
+ * @return {@link UserAccount}
+ */
+ @Override
+ public UserAccount writeToDb(final UserAccount userAccount) {
+ db.put(userAccount.getUserId(), userAccount);
+ return userAccount;
+ }
+
+ /**
+ * Update reecord in DB.
+ *
+ * @param userAccount {@link UserAccount}
+ * @return {@link UserAccount}
+ */
+ @Override
+ public UserAccount updateDb(final UserAccount userAccount) {
+ return writeToDb(userAccount);
+ }
+
+ /**
+ * Update.
+ *
+ * @param userAccount {@link UserAccount}
+ * @return {@link UserAccount}
+ */
+ @Override
+ public UserAccount upsertDb(final UserAccount userAccount) {
+ return updateDb(userAccount);
+ }
+}
diff --git a/caching/src/main/java/com/iluwatar/caching/database/package-info.java b/caching/src/main/java/com/iluwatar/caching/database/package-info.java
new file mode 100644
index 000000000000..631cb4c584cd
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/database/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/** Database classes. */
+package com.iluwatar.caching.database;
diff --git a/caching/src/main/java/com/iluwatar/caching/package-info.java b/caching/src/main/java/com/iluwatar/caching/package-info.java
new file mode 100644
index 000000000000..e7a60b3e95b5
--- /dev/null
+++ b/caching/src/main/java/com/iluwatar/caching/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching;
diff --git a/caching/src/test/java/com/iluwatar/caching/AppTest.java b/caching/src/test/java/com/iluwatar/caching/AppTest.java
new file mode 100644
index 000000000000..35e01edbc37e
--- /dev/null
+++ b/caching/src/test/java/com/iluwatar/caching/AppTest.java
@@ -0,0 +1,43 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests that Caching example runs without errors. */
+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} throws an exception.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/caching/src/test/java/com/iluwatar/caching/CachingTest.java b/caching/src/test/java/com/iluwatar/caching/CachingTest.java
new file mode 100644
index 000000000000..d17cff5bd2ef
--- /dev/null
+++ b/caching/src/test/java/com/iluwatar/caching/CachingTest.java
@@ -0,0 +1,69 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/** Application test */
+class CachingTest {
+ private App app;
+
+ /** Setup of application test includes: initializing DB connection and cache size/capacity. */
+ @BeforeEach
+ void setUp() {
+ // VirtualDB (instead of MongoDB) was used in running the JUnit tests
+ // to avoid Maven compilation errors. Set flag to true to run the
+ // tests with MongoDB (provided that MongoDB is installed and socket
+ // connection is open).
+ app = new App(false);
+ }
+
+ @Test
+ void testReadAndWriteThroughStrategy() {
+ assertNotNull(app);
+ app.useReadAndWriteThroughStrategy();
+ }
+
+ @Test
+ void testReadThroughAndWriteAroundStrategy() {
+ assertNotNull(app);
+ app.useReadThroughAndWriteAroundStrategy();
+ }
+
+ @Test
+ void testReadThroughAndWriteBehindStrategy() {
+ assertNotNull(app);
+ app.useReadThroughAndWriteBehindStrategy();
+ }
+
+ @Test
+ void testCacheAsideStrategy() {
+ assertNotNull(app);
+ app.useCacheAsideStrategy();
+ }
+}
diff --git a/caching/src/test/java/com/iluwatar/caching/database/MongoDbTest.java b/caching/src/test/java/com/iluwatar/caching/database/MongoDbTest.java
new file mode 100644
index 000000000000..87cc1ed6f587
--- /dev/null
+++ b/caching/src/test/java/com/iluwatar/caching/database/MongoDbTest.java
@@ -0,0 +1,110 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.caching.database;
+
+import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
+import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
+import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.*;
+
+import com.iluwatar.caching.UserAccount;
+import com.iluwatar.caching.constants.CachingConstants;
+import com.mongodb.client.FindIterable;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import org.bson.Document;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+class MongoDbTest {
+ private static final String ID = "123";
+ private static final String NAME = "Some user";
+ private static final String ADDITIONAL_INFO = "Some app Info";
+
+ @Mock MongoDatabase db;
+ private MongoDb mongoDb = new MongoDb();
+
+ private UserAccount userAccount;
+
+ @BeforeEach
+ void init() {
+ db = mock(MongoDatabase.class);
+ mongoDb.setDb(db);
+ userAccount = new UserAccount(ID, NAME, ADDITIONAL_INFO);
+ }
+
+ @Test
+ void connect() {
+ assertDoesNotThrow(() -> mongoDb.connect());
+ }
+
+ @Test
+ void readFromDb() {
+ Document document =
+ new Document(USER_ID, ID).append(USER_NAME, NAME).append(ADD_INFO, ADDITIONAL_INFO);
+ MongoCollection mongoCollection = mock(MongoCollection.class);
+ when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
+
+ FindIterable findIterable = mock(FindIterable.class);
+ when(mongoCollection.find(any(Document.class))).thenReturn(findIterable);
+
+ when(findIterable.first()).thenReturn(document);
+
+ assertEquals(mongoDb.readFromDb(ID), userAccount);
+ }
+
+ @Test
+ void writeToDb() {
+ MongoCollection mongoCollection = mock(MongoCollection.class);
+ when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
+ assertDoesNotThrow(
+ () -> {
+ mongoDb.writeToDb(userAccount);
+ });
+ }
+
+ @Test
+ void updateDb() {
+ MongoCollection mongoCollection = mock(MongoCollection.class);
+ when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
+ assertDoesNotThrow(
+ () -> {
+ mongoDb.updateDb(userAccount);
+ });
+ }
+
+ @Test
+ void upsertDb() {
+ MongoCollection mongoCollection = mock(MongoCollection.class);
+ when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
+ assertDoesNotThrow(
+ () -> {
+ mongoDb.upsertDb(userAccount);
+ });
+ }
+}
diff --git a/callback/README.md b/callback/README.md
new file mode 100644
index 000000000000..923132ac5b6a
--- /dev/null
+++ b/callback/README.md
@@ -0,0 +1,135 @@
+---
+title: "Callback Pattern in Java: Mastering Asynchronous Communication"
+shortTitle: Callback
+description: "Learn about the Java Callback Design Pattern, including its intent, usage scenarios, benefits, trade-offs, and real-world examples. Understand how to implement and effectively use callbacks in your Java applications."
+category: Functional
+language: en
+tag:
+ - Asynchronous
+ - Decoupling
+ - Idiom
+ - Reactive
+---
+
+## Also known as
+
+* Call-After
+* Event-Subscription
+* Listener
+
+## Intent of Callback Design Pattern
+
+The Java Callback Design Pattern is a piece of executable code passed as an argument to other code, which is expected to call back (execute) the argument at a convenient time.
+
+## Detailed Explanation of Callback Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world analogy for the Callback design pattern can be found in the restaurant industry. Imagine a situation where you place an order at a busy restaurant. Instead of waiting at the counter for your food to be ready, you provide the cashier with your phone number. Once your order is prepared, the kitchen staff calls or sends a text message to notify you that your meal is ready for pickup.
+>
+> In this analogy, placing your order is analogous to initiating an asynchronous task. Providing your phone number is akin to passing a callback function. The kitchen preparing your order represents the asynchronous processing, and the notification you receive is the callback being executed, allowing you to retrieve your meal without having to wait idly. This separation of task initiation and task completion is the essence of the Callback design pattern.
+
+In plain words
+
+> Callback is a method passed to an executor which will be called at a defined moment.
+
+Wikipedia says
+
+> In computer programming, a callback, also known as a "call-after" function, is any executable code that is passed as an argument to other code; that other code is expected to call back (execute) the argument at a given time.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Callback Pattern in Java
+
+We need to be notified after the executing task has finished. We pass a callback method for the executor and wait for it to call back on us.
+
+`Callback` is a simple interface with single method.
+
+```java
+public interface Callback {
+
+ void call();
+}
+```
+
+Next we define `Task` that will execute the callback after the task execution has finished.
+
+```java
+public abstract class Task {
+
+ final void executeWith(Callback callback) {
+ execute();
+ Optional.ofNullable(callback).ifPresent(Callback::call);
+ }
+
+ public abstract void execute();
+}
+
+@Slf4j
+public final class SimpleTask extends Task {
+
+ @Override
+ public void execute() {
+ LOGGER.info("Perform some important activity and after call the callback method.");
+ }
+}
+```
+
+Finally, here's how we execute a task and receive a callback when it's finished.
+
+```java
+public static void main(final String[] args) {
+ var task = new SimpleTask();
+ task.executeWith(() -> LOGGER.info("I'm done now."));
+}
+```
+
+Program output:
+
+```
+17:12:11.680 [main] INFO com.iluwatar.callback.SimpleTask -- Perform some important activity and after call the callback method.
+17:12:11.682 [main] INFO com.iluwatar.callback.App -- I'm done now.
+```
+
+## When to Use the Callback Pattern in Java
+
+Use the Callback pattern when
+
+* Asynchronous event handling in GUI applications or event-driven systems
+* Implementing notification mechanisms where certain events need to trigger actions in other components.
+* Decoupling modules or components that need to interact without having a direct dependency on each other
+
+## Real-World Applications of Callback Pattern in Java
+
+* GUI frameworks often use callbacks for event handling, such as user interactions (clicks, key presses)
+* Node.js heavily relies on callbacks for non-blocking I/O operations
+* Frameworks that deal with asynchronous operations, like Promises in JavaScript, use callbacks to handle the resolution or rejection of asynchronous tasks
+* [CyclicBarrier](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept a callback that will be triggered every time a barrier is tripped.
+
+## Benefits and Trade-offs of Callback Pattern
+
+Benefits:
+
+* Decouples the execution logic of an operation from the signaling or notification logic, enhancing modularity and reusability
+* Facilitates asynchronous processing, improving the responsiveness and scalability of applications
+* Enables a reactive programming model where components can react to events as they occur
+
+Trade-offs:
+
+* Callback hell or pyramid of doom: Deeply nested callbacks can lead to code that is hard to read and maintain
+* Inversion of control can lead to harder-to-follow code flow, making debugging more challenging
+* Potential issues with error handling, especially in languages or environments where exceptions are used, as errors might need to be propagated through callbacks
+
+## Related Java Design Patterns
+
+* [Command](https://java-design-patterns.com/patterns/command/): Callbacks can be implemented as Command objects in scenarios where more flexibility or statefulness is required in the callback operation
+* [Observer](https://java-design-patterns.com/patterns/observer/): Callbacks can be seen as a more dynamic and lightweight form of the Observer pattern, with the ability to subscribe and unsubscribe callback functions dynamically
+* [Promise](https://java-design-patterns.com/patterns/promise/): In some languages or frameworks, Promises or Futures can be used to handle asynchronous operations more cleanly, often using callbacks for success or failure cases
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
diff --git a/callback/etc/callback-sequence-diagram.png b/callback/etc/callback-sequence-diagram.png
new file mode 100644
index 000000000000..5922734d8fd2
Binary files /dev/null and b/callback/etc/callback-sequence-diagram.png differ
diff --git a/callback/etc/callback.png b/callback/etc/callback.png
index a81745871a64..7b499f79fcaa 100644
Binary files a/callback/etc/callback.png and b/callback/etc/callback.png differ
diff --git a/callback/etc/callback.urm.puml b/callback/etc/callback.urm.puml
new file mode 100644
index 000000000000..2d213eda800c
--- /dev/null
+++ b/callback/etc/callback.urm.puml
@@ -0,0 +1,23 @@
+@startuml
+package com.iluwatar.callback {
+ class App {
+ - LOGGER : Logger {static}
+ - App()
+ + main(args : String[]) {static}
+ }
+ interface Callback {
+ + call() {abstract}
+ }
+ class SimpleTask {
+ - LOGGER : Logger {static}
+ + SimpleTask()
+ + execute()
+ }
+ abstract class Task {
+ + Task()
+ + execute() {abstract}
+ ~ executeWith(callback : Callback)
+ }
+}
+SimpleTask --|> Task
+@enduml
\ No newline at end of file
diff --git a/callback/pom.xml b/callback/pom.xml
index 89759e6d8450..772615f457f9 100644
--- a/callback/pom.xml
+++ b/callback/pom.xml
@@ -1,18 +1,70 @@
-
-
+
+
+
4.0.0
com.iluwatar
java-design-patterns
- 1.4.0
+ 1.26.0-SNAPSHOT
callback
- junit
- junit
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
test
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+