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
index b881ee7ac0f6..607b4a7f7913 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,13 @@
* 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
/**
* The Abstract Document pattern enables handling additional, non-static properties. This pattern
@@ -38,30 +38,37 @@
* 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 {
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
-
/**
- * Executes the App.
+ * Program entry point.
+ *
+ * @param args command line args
*/
- public App() {
+ 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 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 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 carProperties =
+ Map.of(
+ Property.MODEL.toString(),
+ "300SL",
+ Property.PRICE.toString(),
+ 10000L,
+ Property.PARTS.toString(),
+ List.of(wheelProperties, doorProperties));
var car = new Car(carProperties);
@@ -69,20 +76,13 @@ public App() {
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))
- );
- }
-
- /**
- * Program entry point.
- *
- * @param args command line args
- */
- public static void main(String[] args) {
- new App();
+ 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
index d0eb85f34c78..79a51b610337 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,22 +22,19 @@
* 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.
- */
+/** Document interface. */
public interface Document {
/**
* Puts the value related to the key.
*
- * @param key element key
+ * @param key element key
* @param value element value
* @return Void
*/
@@ -52,7 +51,7 @@ public interface Document {
/**
* Gets the stream of child documents.
*
- * @param key element key
+ * @param key element key
* @param constructor constructor of child class
* @return child documents
*/
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
index bf68d40e57fe..93fbbb9c1eae 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,19 +22,15 @@
* 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.
- */
+/** 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
index 76d7d743139d..6f517588e5a0 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,20 +22,16 @@
* 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.
- */
+/** 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
index 8ecfa85fb5eb..8bffa753e6ef 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,21 +22,16 @@
* 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.
- */
+/** 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
index 9a95f2a514db..ce876e5faf54 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,21 +22,16 @@
* 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.
- */
+/** 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
index b1d5bd6b563f..5e0f49df7b7b 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,21 +22,16 @@
* 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.
- */
+/** 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
index 996598a92712..6eec08b0d2a4 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java
+++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,19 +22,15 @@
* 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.
- */
+/** 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
index 640f0ed83f1d..3e0d6d10ab1f 100644
--- 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
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,13 +22,12 @@
* 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.
- */
+/** Enum To Describe Property type. */
public enum Property {
-
- PARTS, TYPE, PRICE, MODEL
+ 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
index d7fe5688d262..a098517c3a69 100644
--- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,43 +22,38 @@
* 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 static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+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
- */
-public class AbstractDocumentTest {
+/** AbstractDocument test class */
+class AbstractDocumentTest {
private static final String KEY = "key";
private static final String VALUE = "value";
- private class DocumentImplementation extends AbstractDocument {
+ private static class DocumentImplementation extends AbstractDocument {
DocumentImplementation(Map properties) {
super(properties);
}
}
- private DocumentImplementation document = new DocumentImplementation(new HashMap<>());
+ private final DocumentImplementation document = new DocumentImplementation(new HashMap<>());
@Test
- public void shouldPutAndGetValue() {
+ void shouldPutAndGetValue() {
document.put(KEY, VALUE);
assertEquals(VALUE, document.get(KEY));
}
@Test
- public void shouldRetrieveChildren() {
+ void shouldRetrieveChildren() {
var children = List.of(Map.of(), Map.of());
document.put(KEY, children);
@@ -67,18 +64,67 @@ public void shouldRetrieveChildren() {
}
@Test
- public void shouldRetrieveEmptyStreamForNonExistingChildren() {
+ void shouldRetrieveEmptyStreamForNonExistingChildren() {
var children = document.children(KEY, DocumentImplementation::new);
assertNotNull(children);
assertEquals(0, children.count());
}
@Test
- public void shouldIncludePropsInToString() {
+ 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
index aed63f303181..16dcba0db37f 100644
--- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,19 +22,23 @@
* 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
- */
-public class AppTest {
+/** 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
- public void shouldExecuteAppWithoutException() {
- App.main(null);
+ 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
index 5b1311ff6af9..fc29dea45c43 100644
--- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* 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;
@@ -32,10 +33,8 @@
import java.util.Map;
import org.junit.jupiter.api.Test;
-/**
- * Test for Part and Car
- */
-public class DomainTest {
+/** 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";
@@ -45,12 +44,12 @@ public class DomainTest {
private static final long TEST_CAR_PRICE = 1L;
@Test
- public 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
- );
+ 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());
@@ -58,16 +57,15 @@ public void shouldConstructPart() {
}
@Test
- public 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())
- );
+ 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
index 6a840f316974..ee9823a39a5f 100644
--- a/abstract-factory/README.md
+++ b/abstract-factory/README.md
@@ -1,24 +1,32 @@
---
-layout: pattern
-title: Abstract Factory
-folder: abstract-factory
-permalink: /patterns/abstract-factory/
-categories: Creational
-tags:
- - Gang of Four
+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
-Provide an interface for creating families of related or dependent
-objects without specifying their concrete classes.
+* Kit
-## Explanation
-Real world example
+## Intent of Abstract Factory Design Pattern
-> To create a kingdom we need objects with common theme. Elven kingdom needs an Elven king, Elven castle and Elven army whereas Orcish kingdom needs an Orcish king, Orcish castle and Orcish army. There is a dependency between the objects in the kingdom.
+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
@@ -28,174 +36,192 @@ 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
-**Programmatic Example**
+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
+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();
+ String getDescription();
}
+
public interface King {
- String getDescription();
+ String getDescription();
}
+
public interface Army {
- String getDescription();
+ String getDescription();
}
// Elven implementations ->
public class ElfCastle implements Castle {
- static final String DESCRIPTION = "This is the Elven castle!";
- @Override
- public String getDescription() {
- return DESCRIPTION;
- }
+ 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;
- }
+ 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;
- }
-}
+ static final String DESCRIPTION = "This is the elven Army!";
-// Orcish implementations similarly...
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
+// Orcish implementations similarly -> ...
```
-Then we have the abstraction and implementations for the kingdom factory
+Then we have the abstraction and implementations for the kingdom factory.
```java
public interface KingdomFactory {
- Castle createCastle();
- King createKing();
- Army createArmy();
+ Castle createCastle();
+
+ King createKing();
+
+ Army createArmy();
}
public class ElfKingdomFactory implements KingdomFactory {
- public Castle createCastle() {
- return new ElfCastle();
- }
- public King createKing() {
- return new ElfKing();
- }
- public Army createArmy() {
- return new ElfArmy();
- }
-}
-public class OrcKingdomFactory implements KingdomFactory {
- public Castle createCastle() {
- return new OrcCastle();
- }
- public King createKing() {
- return new OrcKing();
- }
- public Army createArmy() {
- return new OrcArmy();
- }
-}
-```
+ @Override
+ public Castle createCastle() {
+ return new ElfCastle();
+ }
-Now we have our abstract factory that lets us make family of related objects i.e. Elven kingdom factory creates Elven castle, king and army etc.
+ @Override
+ public King createKing() {
+ return new ElfKing();
+ }
-```java
-var factory = new ElfKingdomFactory();
-var castle = factory.createCastle();
-var king = factory.createKing();
-var army = factory.createArmy();
-
-castle.getDescription(); // Output: This is the Elven castle!
-king.getDescription(); // Output: This is the Elven king!
-army.getDescription(); // Output: This is the Elven Army!
+ @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 (Army, King, Castle).
-In this example, we also used an enum to parameterize which type of kingdom factory the client will ask for.
+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) {
- switch (type) {
- case ELF:
- return new ElfKingdomFactory();
- case ORC:
- return new OrcKingdomFactory();
- default:
- throw new IllegalArgumentException("KingdomType not supported.");
+ public enum KingdomType {
+ ELF, ORC
+ }
+
+ public static KingdomFactory makeFactory(KingdomType type) {
+ return switch (type) {
+ case ELF -> new ElfKingdomFactory();
+ case ORC -> new OrcKingdomFactory();
+ };
}
- }
}
+```
-public static void main(String[] args) {
- var app = new App();
+Here is the main function of our example application:
- LOGGER.info("Elf Kingdom");
- app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
- LOGGER.info(app.getArmy().getDescription());
- LOGGER.info(app.getCastle().getDescription());
- LOGGER.info(app.getKing().getDescription());
+```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());
+```
- LOGGER.info("Orc Kingdom");
- app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
- -- similar use of the orc factory
-}
+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!
```
-## Class diagram
-
+## When to Use the Abstract Factory Pattern in Java
+Use the Abstract Factory pattern in Java when:
-## Applicability
-Use the Abstract Factory pattern 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.
-* a system should be independent of how its products are created, composed and represented
-* a system should be configured with one of multiple families of products
-* a family of related product objects is designed to be used together, and you need to enforce this constraint
-* you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations
-* the lifetime of the dependency is conceptually shorter than the lifetime of the consumer.
-* you need a run-time value to construct a particular dependency
-* you want to decide which product to call from a family at runtime.
-* you need to supply one or more parameters only known at run-time before you can resolve a dependency.
-* when you need consistency among products
-* you don’t want to change existing code when adding new products or families of products to the program.
+## Abstract Factory Pattern Java Tutorials
-## Use Cases:
+* [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)
-* Selecting to call the appropriate implementation of FileSystemAcmeService or DatabaseAcmeService or NetworkAcmeService at runtime.
-* Unit test case writing becomes much easier
-* UI tools for different OS
+## Benefits and Trade-offs of Abstract Factory Pattern
-## Consequences:
+Benefits:
-* Dependency injection in java hides the service class dependencies that can lead to runtime errors that would have been caught at compile time.
-* While the pattern is great when creating predefined objects, adding the new ones might be challenging.
-* The code may become more complicated than it should be, since a lot of new interfaces and classes are introduced along with the pattern.
+* Flexibility: Easily switch between product families without code modifications.
+* Decoupling: Client code only interacts with abstract interfaces, promoting portability and maintainability.
-## Tutorial
-* [Abstract Factory Pattern Tutorial](https://www.journaldev.com/1418/abstract-factory-design-pattern-in-java)
+* Reusability: Abstract factories and products facilitate component reuse across projects.
+* Maintainability: Changes to individual product families are localized, simplifying updates.
-## Real world examples
+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--)
-## Credits
+## 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](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
+* [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/pom.xml b/abstract-factory/pom.xml
index 614b26232d99..60fbf72eff54 100644
--- a/abstract-factory/pom.xml
+++ b/abstract-factory/pom.xml
@@ -1,56 +1,72 @@
-
-
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.23.0-SNAPSHOT
-
- abstract-factory
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ abstract-factory
+
+
+ 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
-
-
-
-
-
-
-
-
+
+ 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 e158ece74b48..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-import com.iluwatar.abstractfactory.App.FactoryMaker.KingdomType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
/**
* The Abstract Factory pattern provides a way to encapsulate a group of individual factories that
@@ -39,87 +39,13 @@
*
*
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.
+ * both concrete implementations to create a king, a castle, and an army.
*/
-public class App {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+@Slf4j
+@Getter
+public class App implements Runnable {
- private King king;
- private Castle castle;
- private Army army;
-
- /**
- * Creates kingdom.
- */
- public void createKingdom(final KingdomFactory factory) {
- setKing(factory.createKing());
- setCastle(factory.createCastle());
- setArmy(factory.createArmy());
- }
-
- King getKing(final KingdomFactory factory) {
- return factory.createKing();
- }
-
- public King getKing() {
- return king;
- }
-
- private void setKing(final King king) {
- this.king = king;
- }
-
- Castle getCastle(final KingdomFactory factory) {
- return factory.createCastle();
- }
-
- public Castle getCastle() {
- return castle;
- }
-
- private void setCastle(final Castle castle) {
- this.castle = castle;
- }
-
- Army getArmy(final KingdomFactory factory) {
- return factory.createArmy();
- }
-
- public Army getArmy() {
- return army;
- }
-
- private void setArmy(final Army army) {
- this.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) {
- switch (type) {
- case ELF:
- return new ElfKingdomFactory();
- case ORC:
- return new OrcKingdomFactory();
- default:
- throw new IllegalArgumentException("KingdomType not supported.");
- }
- }
- }
+ private final Kingdom kingdom = new Kingdom();
/**
* Program entry point.
@@ -127,19 +53,34 @@ public static KingdomFactory makeFactory(KingdomType type) {
* @param args command line args
*/
public static void main(String[] args) {
-
var app = new App();
+ app.run();
+ }
- LOGGER.info("Elf Kingdom");
- app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
- LOGGER.info(app.getArmy().getDescription());
- LOGGER.info(app.getCastle().getDescription());
- LOGGER.info(app.getKing().getDescription());
+ @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());
+ }
- LOGGER.info("Orc Kingdom");
- app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
- LOGGER.info(app.getArmy().getDescription());
- LOGGER.info(app.getCastle().getDescription());
- LOGGER.info(app.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());
}
-}
\ No newline at end of file
+}
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 51f69a82256c..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * Army interface.
- */
+/** 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 c75eb32ef333..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * Castle interface.
- */
+/** 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 6d2da97ccfd9..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * ElfArmy.
- */
+/** ElfArmy. */
public class ElfArmy implements Army {
- static final String DESCRIPTION = "This is the Elven Army!";
+ static final String DESCRIPTION = "This is the elven army!";
@Override
public String getDescription() {
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 5f2b6ed2a5e8..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * ElfCastle.
- */
+/** ElfCastle. */
public class ElfCastle implements Castle {
- static final String DESCRIPTION = "This is the Elven castle!";
+ static final String DESCRIPTION = "This is the elven castle!";
@Override
public String getDescription() {
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 f7c4c614667f..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * ElfKing.
- */
+/** ElfKing. */
public class ElfKing implements King {
- static final String DESCRIPTION = "This is the Elven king!";
+ static final String DESCRIPTION = "This is the elven king!";
@Override
public String getDescription() {
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 4493d2d08a39..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * ElfKingdomFactory concrete factory.
- */
+/** ElfKingdomFactory concrete factory. */
public class ElfKingdomFactory implements KingdomFactory {
@Override
@@ -42,5 +41,4 @@ public King createKing() {
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 97e5f676f29f..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * King interface.
- */
+/** 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 a72dbf78cbe0..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * KingdomFactory factory interface.
- */
+/** KingdomFactory factory interface. */
public interface KingdomFactory {
Castle createCastle();
@@ -33,5 +32,4 @@ public interface KingdomFactory {
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 ae0bd5c3e24a..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * OrcArmy.
- */
+/** OrcArmy. */
public class OrcArmy implements Army {
- static final String DESCRIPTION = "This is the Orc Army!";
+ static final String DESCRIPTION = "This is the orc army!";
@Override
public String getDescription() {
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 458a61bf9920..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * OrcCastle.
- */
+/** OrcCastle. */
public class OrcCastle implements Castle {
- static final String DESCRIPTION = "This is the Orc castle!";
+ static final String DESCRIPTION = "This is the orc castle!";
@Override
public String getDescription() {
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 f73ada9b080e..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * OrcKing.
- */
+/** OrcKing. */
public class OrcKing implements King {
- static final String DESCRIPTION = "This is the Orc king!";
+ static final String DESCRIPTION = "This is the orc king!";
@Override
public String getDescription() {
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 ae7c744be72b..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-/**
- * OrcKingdomFactory concrete factory.
- */
+/** OrcKingdomFactory concrete factory. */
public class OrcKingdomFactory implements KingdomFactory {
@Override
diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
index be83cc315627..b5dde940c464 100644
--- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
+++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,68 +22,71 @@
* 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 com.iluwatar.abstractfactory.App.FactoryMaker;
-import com.iluwatar.abstractfactory.App.FactoryMaker.KingdomType;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-/**
- * Test for abstract factory.
- */
-public class AbstractFactoryTest {
+/** Tests for abstract factory. */
+class AbstractFactoryTest {
- private App app = new App();
- private KingdomFactory elfFactory;
- private KingdomFactory orcFactory;
-
- @BeforeEach
- public void setUp() {
- elfFactory = FactoryMaker.makeFactory(KingdomType.ELF);
- orcFactory = FactoryMaker.makeFactory(KingdomType.ORC);
- }
+ private final App app = new App();
@Test
- public void king() {
- final var elfKing = app.getKing(elfFactory);
+ 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());
- final var orcKing = app.getKing(orcFactory);
+
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var orcKing = kingdom.getKing();
assertTrue(orcKing instanceof OrcKing);
assertEquals(OrcKing.DESCRIPTION, orcKing.getDescription());
}
@Test
- public void castle() {
- final var elfCastle = app.getCastle(elfFactory);
+ 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());
- final var orcCastle = app.getCastle(orcFactory);
+
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var orcCastle = kingdom.getCastle();
assertTrue(orcCastle instanceof OrcCastle);
assertEquals(OrcCastle.DESCRIPTION, orcCastle.getDescription());
}
@Test
- public void army() {
- final var elfArmy = app.getArmy(elfFactory);
+ 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());
- final var orcArmy = app.getArmy(orcFactory);
+
+ app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+ final var orcArmy = kingdom.getArmy();
assertTrue(orcArmy instanceof OrcArmy);
assertEquals(OrcArmy.DESCRIPTION, orcArmy.getDescription());
}
@Test
- public void createElfKingdom() {
- app.createKingdom(elfFactory);
- final var king = app.getKing();
- final var castle = app.getCastle();
- final var army = app.getArmy();
+ 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);
@@ -91,11 +96,13 @@ public void createElfKingdom() {
}
@Test
- public void createOrcKingdom() {
- app.createKingdom(orcFactory);
- final var king = app.getKing();
- final var castle = app.getCastle();
- final var army = app.getArmy();
+ 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);
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 4036cc9b8e34..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,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,17 +22,18 @@
* 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;
-/**
- * Tests that Abstract Factory example runs without errors.
- */
-public class AppTest {
+/** Check whether the execution of the main method in {@link App} throws an exception. */
+class AppTest {
+
@Test
- public void test() {
- App.main(new String[]{});
+ 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
index f293e4393f20..fb57d5681fd4 100644
--- a/acyclic-visitor/README.md
+++ b/acyclic-visitor/README.md
@@ -1,20 +1,150 @@
---
-layout: pattern
-title: Acyclic Visitor
-folder: acyclic-visitor
-permalink: /patterns/acyclic-visitor/
-categories: Behavioral
-tags:
- - Extensibility
+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
-Allow new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating the troublesome dependency cycles that are inherent to the GOF VISITOR Pattern.
+## Intent of Acyclic Visitor Design Pattern
-## Class diagram
-
+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
-## Applicability
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.
@@ -23,20 +153,34 @@ This pattern can be used:
* 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.
-## Consequences
-The good:
+## 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:
-* No dependency cycles between class hierarchies.
-* No need to recompile all the visitors if a new one is added.
-* Does not cause compilation failure in existing visitors if class hierarchy has a new member.
+* Increased complexity: Can introduce additional complexity with the need for multiple visitor interfaces.
+* Maintenance overhead: Modifying the object hierarchy requires updating all visitors.
-The bad:
+## Related Java Design Patterns
-* Violates the principle of least surprise or Liskov's Substitution principle by showing that it can accept all visitors but actually only being interested in particular visitors.
-* Parallel hierarchy of visitors has to be created for all members in visitable class hierarchy.
+* [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.
-## Related patterns
-* [Visitor Pattern](../visitor/)
+## References and Credits
-## Credits
-* [Acyclic Visitor](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
+* [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.ucls b/acyclic-visitor/etc/Acyclic Visitor.ucls
deleted file mode 100644
index 03b6c77dd120..000000000000
--- a/acyclic-visitor/etc/Acyclic Visitor.ucls
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
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
index 636532c4d5d6..7b4df13d80f8 100644
Binary files a/acyclic-visitor/etc/acyclic-visitor.png and b/acyclic-visitor/etc/acyclic-visitor.png differ
diff --git a/acyclic-visitor/pom.xml b/acyclic-visitor/pom.xml
index 2179774c2eea..b4f5646b71c0 100644
--- a/acyclic-visitor/pom.xml
+++ b/acyclic-visitor/pom.xml
@@ -1,65 +1,59 @@
-
-
- 4.0.0
+
+
-
- org.assertj
- assertj-core
- 3.9.1
- test
-
-
-
- uk.org.lidalia
- slf4j-test
- 1.0.0
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.mockito
- mockito-all
- 1.9.5
- test
-
-
-
+ 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.
+
+-->
+
+ 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
+
+
+
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
index 354c4db74962..a3b1679a2d9f 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,13 +22,10 @@
* 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 {
-
-}
+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
index 866abc3b736a..3b7c6cd61e4b 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
/**
@@ -36,9 +37,7 @@
*/
public class App {
- /**
- * Program's entry point.
- */
+ /** Program's entry point. */
public static void main(String[] args) {
var conUnix = new ConfigureForUnixVisitor();
var conDos = new ConfigureForDosVisitor();
@@ -49,6 +48,6 @@ public static void main(String[] args) {
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
+ 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
index f9df385295d3..267a8d66ac45 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,20 +22,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
/**
- * ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos
- * manufacturer.
+ * ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos manufacturer.
*/
+@Slf4j
public class ConfigureForDosVisitor implements AllModemVisitor {
- private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForDosVisitor.class);
-
@Override
public void visit(Hayes hayes) {
LOGGER.info(hayes + " 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
index 3d14eff8fcee..d9fd14f69435 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,22 +22,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+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 {
- private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForUnixVisitor.class);
-
@Override
public void visit(Zoom zoom) {
LOGGER.info(zoom + " used with Unix configurator.");
}
-}
\ No newline at end of file
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
index b49a6234cfd6..e0b2fcc2b530 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,22 +22,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
-/**
- * Hayes class implements its accept method.
- */
-public class Hayes extends Modem {
+/** Hayes class implements its accept method. */
+@Slf4j
+public class Hayes implements Modem {
- private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForDosVisitor.class);
-
- /**
- * Accepts all visitors but honors only HayesVisitor.
- */
+ /** Accepts all visitors but honors only HayesVisitor. */
@Override
public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof HayesVisitor) {
@@ -43,12 +38,9 @@ public void accept(ModemVisitor modemVisitor) {
} else {
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
}
-
}
- /**
- * Hayes' modem's toString method.
- */
+ /** 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
index 59527d57b2ce..aad9b970994f 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-/**
- * HayesVisitor interface.
- */
+/** 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
index 201712dd1398..8552574453e5 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-/**
- * Modem abstract class.
- */
-public abstract class Modem {
- public abstract void accept(ModemVisitor modemVisitor);
+/** //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
index b4058f237d22..9391a980cd29 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
/**
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
index 3fbaa38dfce6..59b50a54a12f 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,22 +22,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Zoom class implements its accept method.
- */
-public class Zoom extends Modem {
+import lombok.extern.slf4j.Slf4j;
- private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForDosVisitor.class);
+/** Zoom class implements its accept method. */
+@Slf4j
+public class Zoom implements Modem {
- /**
- * Accepts all visitors but honors only ZoomVisitor.
- */
+ /** Accepts all visitors but honors only ZoomVisitor. */
@Override
public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof ZoomVisitor) {
@@ -45,9 +40,7 @@ public void accept(ModemVisitor modemVisitor) {
}
}
- /**
- * Zoom modem's toString method.
- */
+ /** 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
index fd54d8b21ca7..5388ded6f735 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-/**
- * ZoomVisitor interface.
- */
+/** 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
index 4b9a7ec6c1f7..7a21498a63ea 100644
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,18 +22,24 @@
* 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.
- */
-public class AppTest {
+/** 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
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
-}
\ No newline at end of file
+}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitorTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitorTest.java
deleted file mode 100644
index 8847a131e605..000000000000
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitorTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.groups.Tuple.tuple;
-import static uk.org.lidalia.slf4jext.Level.INFO;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-import uk.org.lidalia.slf4jtest.TestLogger;
-import uk.org.lidalia.slf4jtest.TestLoggerFactory;
-
-/**
- * ConfigureForDosVisitor test class
- */
-public class ConfigureForDosVisitorTest {
-
- private TestLogger logger = TestLoggerFactory.getTestLogger(ConfigureForDosVisitor.class);
-
- @Test
- public void testVisitForZoom() {
- var conDos = new ConfigureForDosVisitor();
- var zoom = new Zoom();
-
- conDos.visit(zoom);
-
- assertThat(logger.getLoggingEvents())
- .extracting("level", "message")
- .contains(tuple(INFO, zoom + " used with Dos configurator."));
- }
-
- @Test
- public void testVisitForHayes() {
- var conDos = new ConfigureForDosVisitor();
- var hayes = new Hayes();
-
- conDos.visit(hayes);
-
- assertThat(logger.getLoggingEvents())
- .extracting("level", "message")
- .contains(tuple(INFO, hayes + " used with Dos configurator."));
- }
-
- @AfterEach
- public void clearLoggers() {
- TestLoggerFactory.clear();
- }
-}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java
deleted file mode 100644
index 32067ad385a3..000000000000
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.groups.Tuple.tuple;
-import static uk.org.lidalia.slf4jext.Level.INFO;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-
-import uk.org.lidalia.slf4jtest.TestLogger;
-import uk.org.lidalia.slf4jtest.TestLoggerFactory;
-
-/**
- * ConfigureForUnixVisitor test class
- */
-public class ConfigureForUnixVisitorTest {
-
- private static final TestLogger LOGGER = TestLoggerFactory.getTestLogger(ConfigureForUnixVisitor.class);
-
- @AfterEach
- public void clearLoggers() {
- TestLoggerFactory.clear();
- }
-
- @Test
- public void testVisitForZoom() {
- var conUnix = new ConfigureForUnixVisitor();
- var zoom = new Zoom();
-
- conUnix.visit(zoom);
-
- assertThat(LOGGER.getLoggingEvents())
- .extracting("level", "message")
- .contains(tuple(INFO, zoom + " used with Unix configurator."));
- }
-}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
index 308dd5879329..a989d9287921 100644
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,37 +22,32 @@
* 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.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
-/**
- * Hayes test class
- */
-public class HayesTest {
+/** Hayes test class */
+class HayesTest {
@Test
- public void testAcceptForDos() {
+ void testAcceptForDos() {
var hayes = new Hayes();
var mockVisitor = mock(ConfigureForDosVisitor.class);
-
+
hayes.accept(mockVisitor);
- verify((HayesVisitor)mockVisitor).visit(eq(hayes));
+ verify((HayesVisitor) mockVisitor).visit(eq(hayes));
}
-
+
@Test
- public void testAcceptForUnix() {
+ void testAcceptForUnix() {
var hayes = new Hayes();
var mockVisitor = mock(ConfigureForUnixVisitor.class);
-
+
hayes.accept(mockVisitor);
-
- verifyZeroInteractions(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
index 2dcfcfbbbf80..d5fe79965d47 100644
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,36 +22,32 @@
* 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.Matchers.eq;
-import static org.mockito.Mockito.verify;
+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
- */
-public class ZoomTest {
-
+/** Zoom test class */
+class ZoomTest {
+
@Test
- public void testAcceptForDos() {
+ void testAcceptForDos() {
var zoom = new Zoom();
var mockVisitor = mock(ConfigureForDosVisitor.class);
-
+
zoom.accept(mockVisitor);
- verify((ZoomVisitor)mockVisitor).visit(eq(zoom));
+ verify((ZoomVisitor) mockVisitor).visit(eq(zoom));
}
-
+
@Test
- public void testAcceptForUnix() {
+ void testAcceptForUnix() {
var zoom = new Zoom();
var mockVisitor = mock(ConfigureForUnixVisitor.class);
-
+
zoom.accept(mockVisitor);
- verify((ZoomVisitor)mockVisitor).visit(eq(zoom));
+ verify((ZoomVisitor) mockVisitor).visit(eq(zoom));
}
}
diff --git a/adapter/README.md b/adapter/README.md
index a8a0214be83c..489742494709 100644
--- a/adapter/README.md
+++ b/adapter/README.md
@@ -1,28 +1,31 @@
---
-layout: pattern
-title: Adapter
-folder: adapter
-permalink: /patterns/adapter/
-categories: Structural
-tags:
- - Gang of Four
+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
-Convert the interface of a class into another interface the clients
-expect. Adapter lets classes work together that couldn't otherwise because of
-incompatible interfaces.
+* Wrapper
-## Explanation
+## Intent of Adapter Design Pattern
-Real world example
+The Adapter Design Pattern in Java converts the interface of a class into another interface that clients expect, enabling compatibility.
-> Consider that you have some pictures in your memory card and you need to transfer them to your computer. In order to transfer them you need some kind of adapter that is compatible with your computer ports so that you can attach 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 outlet.
-> Yet another example would be a translator translating words spoken by one person to another
+## 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
@@ -32,102 +35,120 @@ 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.
-**Programmatic Example**
+Sequence diagram
+
+
+
+## Programmatic Example of Adapter Pattern in Java
-Consider a captain that can only use rowing boats and cannot sail at all.
+The Adapter Pattern example in Java shows how a class with an incompatible interface can be adapted to work with another class.
-First we have interfaces `RowingBoat` and `FishingBoat`
+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();
+ void row();
}
+@Slf4j
public class FishingBoat {
- private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class);
- public void sail() {
- LOGGER.info("The fishing boat is sailing");
- }
+ public void sail() {
+ LOGGER.info("The fishing boat is sailing");
+ }
}
```
-And captain expects an implementation of `RowingBoat` interface to be able to move
+The captain expects an implementation of `RowingBoat` interface to be able to move.
```java
public class Captain {
- private RowingBoat rowingBoat;
- // default constructor and setter for rowingBoat
- public Captain(RowingBoat rowingBoat) {
- this.rowingBoat = rowingBoat;
- }
+ private final RowingBoat rowingBoat;
+
+ // default constructor and setter for rowingBoat
+ public Captain(RowingBoat rowingBoat) {
+ this.rowingBoat = rowingBoat;
+ }
- public void row() {
- rowingBoat.row();
- }
+ public void row() {
+ rowingBoat.row();
+ }
}
```
-Now let's say the pirates are coming and our captain needs to escape but there is only fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
+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 static final Logger LOGGER = LoggerFactory.getLogger(FishingBoatAdapter.class);
-
- private FishingBoat boat;
+ private final FishingBoat boat;
- public FishingBoatAdapter() {
- boat = new FishingBoat();
- }
+ public FishingBoatAdapter() {
+ boat = new FishingBoat();
+ }
- @Override
- public void row() {
- boat.sail();
- }
+ @Override
+ public void row() {
+ boat.sail();
+ }
}
```
-And now the `Captain` can use the `FishingBoat` to escape the pirates.
+Now the `Captain` can use the `FishingBoat` to escape the pirates.
```java
-var captain = new Captain(new FishingBoatAdapter());
-captain.row();
+ 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
```
-## Class diagram
-
+## When to Use the Adapter Pattern in Java
-## Applicability
-Use the Adapter pattern when
+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 every one. 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.
+* 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.
-## Consequences:
-Class and object adapters have different trade-offs. A class adapter
+## Adapter Pattern Java Tutorials
-* adapts Adaptee to Target by committing to a concrete Adaptee class. As a consequence, a class adapter won’t work when we want to adapt a class and all its subclasses.
-* let’s Adapter override some of Adaptee’s behavior, since Adapter is a subclass of Adaptee.
-* introduces only one object, and no additional pointer indirection is needed to get to the adaptee.
+* [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/)
-An object adapter
+## Benefits and Trade-offs of Adapter Pattern
-* let’s a single Adapter work with many Adaptees—that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.
-* makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making Adapter refer to the subclass rather than the Adaptee itself.
+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 examples
+## 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
-## Credits
-
-* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
-* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
+* [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/pom.xml b/adapter/pom.xml
index 4c725def8953..6e7f45a515b5 100644
--- a/adapter/pom.xml
+++ b/adapter/pom.xml
@@ -1,61 +1,77 @@
-
-
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.23.0-SNAPSHOT
-
- adapter
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.mockito
- mockito-core
- test
-
-
-
-
-
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ adapter
+
+
+ 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
-
-
-
-
-
-
-
-
+
+ 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 4e3755fb27f2..a4fa74274f74 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/App.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.adapter;
/**
@@ -36,16 +37,15 @@
*
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}.
+ *
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() {
- }
+ private App() {}
/**
* Program entry point.
diff --git a/adapter/src/main/java/com/iluwatar/adapter/Captain.java b/adapter/src/main/java/com/iluwatar/adapter/Captain.java
index b83b13429660..3b771e9d833e 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/Captain.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/Captain.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,29 +22,24 @@
* 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.
+ * The Captain uses {@link RowingBoat} to sail.
+ * This is the client in the pattern.
*/
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
public final class Captain {
private RowingBoat rowingBoat;
- public Captain() {
- }
-
- public Captain(final RowingBoat boat) {
- this.rowingBoat = boat;
- }
-
- void setRowingBoat(final RowingBoat boat) {
- this.rowingBoat = boat;
- }
-
void row() {
rowingBoat.row();
}
-
}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
index 123006c46d42..dd39f88f1ce0 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,23 +22,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.adapter;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import org.slf4j.Logger;
+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 {
- private static final Logger LOGGER = getLogger(FishingBoat.class);
-
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
index 5ccde5c53bcb..8c7e9e88e220 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.adapter;
/**
@@ -29,11 +30,7 @@
*/
public class FishingBoatAdapter implements RowingBoat {
- private FishingBoat boat;
-
- public FishingBoatAdapter() {
- boat = new FishingBoat();
- }
+ private final FishingBoat boat = new FishingBoat();
public final void row() {
boat.sail();
diff --git a/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
index 908036a3f19c..55eeeaf4bd42 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,14 +22,13 @@
* 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.
+ * 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
index d036d86ddb47..c42f2ac86ceb 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/package-info.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/package-info.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,5 +22,4 @@
* 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
index f87073b234f2..bc4984da3d6f 100644
--- a/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java
+++ b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* 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;
@@ -31,10 +32,8 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-/**
- * Test class
- */
-public class AdapterPatternTest {
+/** Tests for the adapter pattern. */
+class AdapterPatternTest {
private Map beans;
@@ -42,11 +41,9 @@ public class AdapterPatternTest {
private static final String ROWING_BEAN = "captain";
- /**
- * This method runs before the test execution and sets the bean objects in the beans Map.
- */
+ /** This method runs before the test execution and sets the bean objects in the beans Map. */
@BeforeEach
- public void setup() {
+ void setup() {
beans = new HashMap<>();
var fishingBoatAdapter = spy(new FishingBoatAdapter());
@@ -64,7 +61,7 @@ public void setup() {
* by the client ({@link Captain} ).
*/
@Test
- public void testAdapter() {
+ void testAdapter() {
var captain = (Captain) beans.get(ROWING_BEAN);
// when captain moves
diff --git a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
index 3bf8e1010a11..a2cc4c22bcaa 100644
--- a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
+++ b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,17 +22,19 @@
* 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.
- */
-public class AppTest {
+/** Tests that Adapter example runs without errors. */
+class AppTest {
+
+ /** Check whether the execution of the main method in {@link App} throws an exception. */
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md
deleted file mode 100644
index 4d64178a66a5..000000000000
--- a/aggregator-microservices/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-layout: pattern
-title: Aggregator Microservices
-folder: aggregator-microservices
-permalink: /patterns/aggregator-microservices/
-categories: Architectural
-tags:
-- Cloud distributed
----
-
-## Intent
-
-The user makes a single call to the Aggregator, and the aggregator then calls each relevant microservice and collects
-the data, apply business logic to it, and further publish is as a REST Endpoint.
-More variations of the aggregator are:
-- Proxy Microservice Design Pattern: A different microservice is called upon the business need.
-- Chained Microservice Design Pattern: In this case each microservice is dependent/ chained to a series
-of other microservices.
-
-## Class diagram
-
-
-
-## Applicability
-
-Use the Aggregator Microservices pattern when you need a unified API for various microservices, regardless the client device.
-
-## Credits
-
-* [Microservice Design Patterns](http://blog.arungupta.me/microservice-design-patterns/)
diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml
deleted file mode 100644
index f4482d0e3f8b..000000000000
--- a/aggregator-microservices/aggregator-service/pom.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.23.0-SNAPSHOT
-
- 4.0.0
- aggregator-service
- jar
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.mockito
- mockito-core
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.aggregator.microservices.App
-
-
-
-
-
-
-
-
-
diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java
deleted file mode 100644
index 3c09c54be3b4..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.aggregator.microservices;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * Spring Boot EntryPoint Class.
- */
-@SpringBootApplication
-public class App {
-
- /**
- * Program entry point.
- *
- * @param args command line args
- */
- public static void main(String[] args) {
- SpringApplication.run(App.class, args);
- }
-}
diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java
deleted file mode 100644
index 3c214a58a9f5..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.aggregator.microservices;
-
-/**
- * Encapsulates all the data for a Product that clients will request.
- */
-public class Product {
-
- /**
- * The title of the product.
- */
- private String title;
-
-
- /**
- * The inventories of the product.
- */
- private int productInventories;
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public int getProductInventories() {
- return productInventories;
- }
-
- public void setProductInventories(int productInventories) {
- this.productInventories = productInventories;
- }
-
-}
diff --git a/aggregator-microservices/aggregator-service/src/main/resources/application.properties b/aggregator-microservices/aggregator-service/src/main/resources/application.properties
deleted file mode 100644
index f9e29f5a7234..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/resources/application.properties
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2019 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.
-#
-server.port=50004
\ No newline at end of file
diff --git a/aggregator-microservices/etc/aggregator-microservice.png b/aggregator-microservices/etc/aggregator-microservice.png
deleted file mode 100644
index ad344a7e1e0f..000000000000
Binary files a/aggregator-microservices/etc/aggregator-microservice.png and /dev/null differ
diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml
deleted file mode 100644
index f99d26b65665..000000000000
--- a/aggregator-microservices/information-microservice/pom.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.23.0-SNAPSHOT
-
- 4.0.0
-
- information-microservice
- jar
-
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.information.microservices.InformationApplication
-
-
-
-
-
-
-
-
-
diff --git a/aggregator-microservices/information-microservice/src/main/resources/application.properties b/aggregator-microservices/information-microservice/src/main/resources/application.properties
deleted file mode 100644
index b953a61da732..000000000000
--- a/aggregator-microservices/information-microservice/src/main/resources/application.properties
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2019 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.
-#
-server.port=51515
\ No newline at end of file
diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml
deleted file mode 100644
index f7899aa8f307..000000000000
--- a/aggregator-microservices/inventory-microservice/pom.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.23.0-SNAPSHOT
-
- 4.0.0
- inventory-microservice
-
- jar
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.inventory.microservices.InventoryApplication
-
-
-
-
-
-
-
-
-
diff --git a/aggregator-microservices/inventory-microservice/src/main/resources/application.properties b/aggregator-microservices/inventory-microservice/src/main/resources/application.properties
deleted file mode 100644
index 9d2021f442d4..000000000000
--- a/aggregator-microservices/inventory-microservice/src/main/resources/application.properties
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2019 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.
-#
-server.port=51516
\ No newline at end of file
diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml
deleted file mode 100644
index a63ec2f126a9..000000000000
--- a/aggregator-microservices/pom.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
- java-design-patterns
- com.iluwatar
- 1.23.0-SNAPSHOT
-
- 4.0.0
- aggregator-microservices
- pom
-
- information-microservice
- aggregator-service
- inventory-microservice
-
-
diff --git a/ambassador/README.md b/ambassador/README.md
index 78b3a885669f..fa08223feb75 100644
--- a/ambassador/README.md
+++ b/ambassador/README.md
@@ -1,37 +1,52 @@
---
-layout: pattern
-title: Ambassador
-folder: ambassador
-permalink: /patterns/ambassador/
-categories: Structural
-tags:
- - Decoupling
+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
-Provide a helper service instance on a client and offload common functionality away from a shared resource.
+## Intent of Ambassador Design Pattern
-## Explanation
-Real world example
+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.
-> 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.
+## 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
-> Using the ambassador pattern, we can implement less-frequent polling from clients along with latency checks and logging.
+> 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 that 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.
+> 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
-**Programmatic Example**
+In this example of the Ambassador Pattern in Java, we demonstrate how to implement latency checks, logging, and retry mechanisms to improve system reliability.
-With the above example in mind we will imitate the functionality in a simple manner. We have an interface implemented by the remote service as well as the ambassador service:
+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;
}
```
@@ -39,9 +54,9 @@ interface RemoteServiceInterface {
A remote services represented as a singleton.
```java
-public class RemoteService implements RemoteServiceInterface {
- private static final Logger LOGGER = LoggerFactory.getLogger(RemoteService.class);
+@Slf4j
+public class RemoteService implements RemoteServiceInterface {
private static RemoteService service = null;
static synchronized RemoteService getRemoteService() {
@@ -51,7 +66,8 @@ public class RemoteService implements RemoteServiceInterface {
return service;
}
- private RemoteService() {}
+ private RemoteService() {
+ }
@Override
public long doRemoteFunction(int value) {
@@ -68,99 +84,108 @@ public class RemoteService implements RemoteServiceInterface {
}
```
-A service ambassador adding additional features such as logging, latency checks
+A service ambassador adds additional features such as logging and latency checks.
```java
-public class ServiceAmbassador implements RemoteServiceInterface {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAmbassador.class);
- private static final int RETRIES = 3;
- private static final int DELAY_MS = 3000;
- ServiceAmbassador() {
- }
-
- @Override
- public long doRemoteFunction(int value) {
- return safeCall(value);
- }
+@Slf4j
+public class ServiceAmbassador implements RemoteServiceInterface {
+ private static final int RETRIES = 3;
+ private static final int DELAY_MS = 3000;
- private long checkLatency(int value) {
- var startTime = System.currentTimeMillis();
- var result = RemoteService.getRemoteService().doRemoteFunction(value);
- var timeTaken = System.currentTimeMillis() - startTime;
+ ServiceAmbassador() {
+ }
- LOGGER.info("Time taken (ms): " + timeTaken);
- return result;
- }
+ @Override
+ public long doRemoteFunction(int value) {
+ return safeCall(value);
+ }
- private long safeCall(int value) {
- var retries = 0;
- var result = (long) FAILURE;
+ private long checkLatency(int value) {
+ var startTime = System.currentTimeMillis();
+ var result = RemoteService.getRemoteService().doRemoteFunction(value);
+ var timeTaken = System.currentTimeMillis() - startTime;
- for (int i = 0; i < RETRIES; i++) {
- if (retries >= RETRIES) {
- return FAILURE;
- }
+ LOGGER.info("Time taken (ms): " + timeTaken);
+ return result;
+ }
- 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);
+ 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;
+ }
}
- } else {
- break;
- }
+ return result;
}
- return result;
- }
}
```
-A client has a local service ambassador used to interact with the remote service:
+A client has a local service ambassador used to interact with the remote service.
```java
-public class Client {
- private static final Logger LOGGER = LoggerFactory.getLogger(Client.class);
- private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
+@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;
- }
+ long useService(int value) {
+ var result = serviceAmbassador.doRemoteFunction(value);
+ LOGGER.info("Service result: " + result);
+ return result;
+ }
}
```
-And here are two clients using the service.
+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);
- }
+ public static void main(String[] args) {
+ var host1 = new Client();
+ var host2 = new Client();
+ host1.useService(12);
+ host2.useService(73);
+ }
}
```
-## Class diagram
-
+Here's the output for running the example:
-## Applicability
-Ambassador is applicable when working with a legacy remote service that cannot
-be modified or would be extremely difficult to modify. Connectivity features can
-be implemented on the client avoiding the need for changes on the remote service.
+```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
+```
-* Ambassador provides a local interface for a remote service.
-* Ambassador provides logging, circuit breaking, retries and security on the client.
+## When to Use the Ambassador Pattern in Java
-## Typical Use Case
+* 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
@@ -168,11 +193,44 @@ be implemented on the client avoiding the need for changes on the remote service
* Offload remote service tasks
* Facilitate network connection
-## Real world examples
+## 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)
-## Credits
+## 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
-* [Ambassador pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/ambassador)
-* [Designing Distributed Systems: Patterns and Paradigms for Scalable, Reliable Services](https://books.google.co.uk/books?id=6BJNDwAAQBAJ&pg=PT35&lpg=PT35&dq=ambassador+pattern+in+real+world&source=bl&ots=d2e7GhYdHi&sig=Lfl_MDnCgn6lUcjzOg4GXrN13bQ&hl=en&sa=X&ved=0ahUKEwjk9L_18rrbAhVpKcAKHX_KA7EQ6AEIWTAI#v=onepage&q=ambassador%20pattern%20in%20real%20world&f=false)
+* [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/pom.xml b/ambassador/pom.xml
index 6d6a9894de20..15e4a07f0dc3 100644
--- a/ambassador/pom.xml
+++ b/ambassador/pom.xml
@@ -1,53 +1,70 @@
-
-
-
- java-design-patterns
- com.iluwatar
- 1.23.0-SNAPSHOT
-
- 4.0.0
- ambassador
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.ambassador.App
-
-
-
-
-
-
-
-
+
+
+
+ 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
index 087fc39c08f9..8de149fe0813 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/App.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,14 @@
* 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
+ *
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
@@ -36,14 +37,11 @@
* capabilities.
*
*
In this example, we will the ({@link ServiceAmbassador}) class represents the ambassador while
- * the
- * ({@link RemoteService}) class represents a remote application.
+ * the ({@link RemoteService}) class represents a remote application.
*/
public class App {
- /**
- * Entry point.
- */
+ /** Entry point. */
public static void main(String[] args) {
var host1 = new Client();
var host2 = new Client();
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/Client.java b/ambassador/src/main/java/com/iluwatar/ambassador/Client.java
index 70d52e799742..0baabf4ffc06 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/Client.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/Client.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,23 +22,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.ambassador;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
-/**
- * A simple Client.
- */
+/** A simple Client. */
+@Slf4j
public class Client {
- private static final Logger LOGGER = LoggerFactory.getLogger(Client.class);
private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
long useService(int value) {
var result = serviceAmbassador.doRemoteFunction(value);
- LOGGER.info("Service result: " + result);
+ 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
index a80806851e6b..d99348040cfe 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,21 +22,17 @@
* 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
-/**
- * A remote legacy application represented by a Singleton implementation.
- */
+/** A remote legacy application represented by a Singleton implementation. */
+@Slf4j
public class RemoteService implements RemoteServiceInterface {
private static final int THRESHOLD = 200;
- private static final Logger LOGGER = LoggerFactory.getLogger(RemoteService.class);
private static RemoteService service = null;
private final RandomProvider randomProvider;
@@ -49,9 +47,7 @@ private RemoteService() {
this(Math::random);
}
- /**
- * This constructor is used for testing purposes only.
- */
+ /** This constructor is used for testing purposes only. */
RemoteService(RandomProvider randomProvider) {
this.randomProvider = randomProvider;
}
@@ -62,7 +58,7 @@ private RemoteService() {
*
* @param value integer value to be multiplied.
* @return if waitTime is less than {@link RemoteService#THRESHOLD}, it returns value * 10,
- * otherwise {@link RemoteServiceInterface#FAILURE}.
+ * otherwise {@link RemoteServiceStatus#FAILURE}.
*/
@Override
public long doRemoteFunction(int value) {
@@ -73,7 +69,10 @@ public long doRemoteFunction(int value) {
sleep(waitTime);
} catch (InterruptedException e) {
LOGGER.error("Thread sleep state interrupted", e);
+ Thread.currentThread().interrupt();
}
- return waitTime <= THRESHOLD ? value * 10 : FAILURE;
+ 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
index 013015936957..aa6012bae33f 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,14 +22,10 @@
* 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 shared by ({@link RemoteService}) and ({@link ServiceAmbassador}). */
interface RemoteServiceInterface {
- int FAILURE = -1;
- long doRemoteFunction(int value) throws Exception;
+ 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
index a9d34581c614..4d310169770b 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,27 +22,25 @@
* 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+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 Logger LOGGER = LoggerFactory.getLogger(ServiceAmbassador.class);
private static final int RETRIES = 3;
private static final int DELAY_MS = 3000;
- ServiceAmbassador() {
- }
+ ServiceAmbassador() {}
@Override
public long doRemoteFunction(int value) {
@@ -52,26 +52,27 @@ private long checkLatency(int value) {
var result = RemoteService.getRemoteService().doRemoteFunction(value);
var timeTaken = System.currentTimeMillis() - startTime;
- LOGGER.info("Time taken (ms): " + timeTaken);
+ LOGGER.info("Time taken (ms): {}", timeTaken);
return result;
}
private long safeCall(int value) {
var retries = 0;
- var result = (long) FAILURE;
+ var result = FAILURE.getRemoteServiceStatusValue();
for (int i = 0; i < RETRIES; i++) {
if (retries >= RETRIES) {
- return FAILURE;
+ return FAILURE.getRemoteServiceStatusValue();
}
- if ((result = checkLatency(value)) == FAILURE) {
- LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
+ 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;
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java b/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java
index 5948472c0062..4eba2fada7fa 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* 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.
- */
+/** 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
index c9a4d09b6965..ddb2d6eff411 100644
--- a/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,18 +22,24 @@
* 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
- */
+/** 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 test() {
- App.main(new String[]{});
+ 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
index 12a93a1cbc53..24603efff9d4 100644
--- a/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,16 +22,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.ambassador;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Test for {@link Client}
- */
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link Client} */
class ClientTest {
@Test
@@ -37,6 +36,7 @@ void test() {
Client client = new Client();
var result = client.useService(10);
- assertTrue(result == 100 || result == RemoteService.FAILURE);
+ 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
index 3cfea262375a..81e4f744128f 100644
--- a/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* 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;
@@ -28,16 +29,14 @@
import com.iluwatar.ambassador.util.RandomProvider;
import org.junit.jupiter.api.Test;
-/**
- * Test for {@link RemoteService}
- */
+/** Test for {@link RemoteService} */
class RemoteServiceTest {
@Test
void testFailedCall() {
var remoteService = new RemoteService(new StaticRandomProvider(0.21));
var result = remoteService.doRemoteFunction(10);
- assertEquals(RemoteServiceInterface.FAILURE, result);
+ assertEquals(RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue(), result);
}
@Test
@@ -48,7 +47,7 @@ void testSuccessfulCall() {
}
private static class StaticRandomProvider implements RandomProvider {
- private double value;
+ private final double value;
StaticRandomProvider(double value) {
this.value = value;
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
index 8eb55b30a12f..0543b2e7e370 100644
--- a/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,21 +22,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.ambassador;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Test for {@link ServiceAmbassador}
- */
+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 == RemoteServiceInterface.FAILURE);
+ 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/api-gateway/README.md b/api-gateway/README.md
deleted file mode 100644
index 3a4f13e35e62..000000000000
--- a/api-gateway/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-layout: pattern
-title: API Gateway
-folder: api-gateway
-permalink: /patterns/api-gateway/
-categories: Architectural
-tags:
-- Cloud distributed
-- Decoupling
----
-
-## Intent
-
-Aggregate calls to microservices in a single location: the API Gateway. The user makes a single
-call to the API Gateway, and the API Gateway then calls each relevant microservice.
-
-## Class diagram
-
-
-## Applicability
-
-Use the API Gateway pattern when
-
-* you're also using the Microservices pattern and need a single point of aggregation for your
-microservice calls
-
-## Credits
-
-* [microservices.io - API Gateway](http://microservices.io/patterns/apigateway.html)
-* [NGINX - Building Microservices: Using an API Gateway](https://www.nginx.com/blog/building-microservices-using-an-api-gateway/)
diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml
deleted file mode 100644
index 744023e900c2..000000000000
--- a/api-gateway/api-gateway-service/pom.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.23.0-SNAPSHOT
-
- 4.0.0
- api-gateway-service
- jar
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.mockito
- mockito-core
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.api.gateway.App
-
-
-
-
-
-
-
-
-
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java
deleted file mode 100644
index fbb8bdfd158b..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.api.gateway;
-
-import javax.annotation.Resource;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * The ApiGateway aggregates calls to microservices based on the needs of the individual clients.
- */
-@RestController
-public class ApiGateway {
-
- @Resource
- private ImageClient imageClient;
-
- @Resource
- private PriceClient priceClient;
-
- /**
- * Retrieves product information that desktop clients need.
- *
- * @return Product information for clients on a desktop
- */
- @RequestMapping(path = "/desktop", method = RequestMethod.GET)
- public DesktopProduct getProductDesktop() {
- var desktopProduct = new DesktopProduct();
- desktopProduct.setImagePath(imageClient.getImagePath());
- desktopProduct.setPrice(priceClient.getPrice());
- return desktopProduct;
- }
-
- /**
- * Retrieves product information that mobile clients need.
- *
- * @return Product information for clients on a mobile device
- */
- @RequestMapping(path = "/mobile", method = RequestMethod.GET)
- public MobileProduct getProductMobile() {
- var mobileProduct = new MobileProduct();
- mobileProduct.setPrice(priceClient.getPrice());
- return mobileProduct;
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java
deleted file mode 100644
index 1d72aaaece37..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.api.gateway;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * With the Microservices pattern, a client may need data from multiple different microservices. If
- * the client called each microservice directly, that could contribute to longer load times, since
- * the client would have to make a network request for each microservice called. Moreover, having
- * the client call each microservice directly ties the client to that microservice - if the internal
- * implementations of the microservices change (for example, if two microservices are combined
- * sometime in the future) or if the location (host and port) of a microservice changes, then every
- * client that makes use of those microservices must be updated.
- *
- * The intent of the API Gateway pattern is to alleviate some of these issues. In the API
- * Gateway pattern, an additional entity (the API Gateway) is placed between the client and the
- * microservices. The job of the API Gateway is to aggregate the calls to the microservices. Rather
- * than the client calling each microservice individually, the client calls the API Gateway a single
- * time. The API Gateway then calls each of the microservices that the client needs.
- *
- *
This implementation shows what the API Gateway pattern could look like for an e-commerce
- * site. The {@link ApiGateway} makes calls to the Image and Price microservices using the {@link
- * ImageClientImpl} and {@link PriceClientImpl} respectively. Customers viewing the site on a
- * desktop device can see both price information and an image of a product, so the {@link
- * ApiGateway} calls both of the microservices and aggregates the data in the {@link DesktopProduct}
- * model. However, mobile users only see price information; they do not see a product image. For
- * mobile users, the {@link ApiGateway} only retrieves price information, which it uses to populate
- * the {@link MobileProduct}.
- */
-@SpringBootApplication
-public class App {
-
- /**
- * Program entry point.
- *
- * @param args command line args
- */
- public static void main(String[] args) {
- SpringApplication.run(App.class, args);
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java
deleted file mode 100644
index 2f790bef5d07..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.api.gateway;
-
-/**
- * Encapsulates all of the information that a desktop client needs to display a product.
- */
-public class DesktopProduct {
- /**
- * The price of the product.
- */
- private String price;
-
- /**
- * The path to the image of the product.
- */
- private String imagePath;
-
- public String getPrice() {
- return price;
- }
-
- public void setPrice(String price) {
- this.price = price;
- }
-
- public String getImagePath() {
- return imagePath;
- }
-
- public void setImagePath(String imagePath) {
- this.imagePath = imagePath;
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java
deleted file mode 100644
index 52dd065ffc35..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.api.gateway;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse.BodyHandlers;
-import org.springframework.stereotype.Component;
-
-/**
- * An adapter to communicate with the Image microservice.
- */
-@Component
-public class ImageClientImpl implements ImageClient {
- /**
- * Makes a simple HTTP Get request to the Image microservice.
- *
- * @return The path to the image
- */
- @Override
- public String getImagePath() {
- var httpClient = HttpClient.newHttpClient();
- var httpGet = HttpRequest.newBuilder()
- .GET()
- .uri(URI.create("http://localhost:50005/image-path"))
- .build();
-
- try {
- var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
- return httpResponse.body();
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
-
- return null;
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java
deleted file mode 100644
index 0dc44a51bced..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.api.gateway;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse.BodyHandlers;
-import org.springframework.stereotype.Component;
-
-/**
- * An adapter to communicate with the Price microservice.
- */
-@Component
-public class PriceClientImpl implements PriceClient {
- /**
- * Makes a simple HTTP Get request to the Price microservice.
- *
- * @return The price of the product
- */
- @Override
- public String getPrice() {
- var httpClient = HttpClient.newHttpClient();
- var httpGet = HttpRequest.newBuilder()
- .GET()
- .uri(URI.create("http://localhost:50006/price"))
- .build();
-
- try {
- var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
- return httpResponse.body();
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
-
- return null;
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/resources/application.properties b/api-gateway/api-gateway-service/src/main/resources/application.properties
deleted file mode 100644
index f9e29f5a7234..000000000000
--- a/api-gateway/api-gateway-service/src/main/resources/application.properties
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2019 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.
-#
-server.port=50004
\ No newline at end of file
diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml
deleted file mode 100644
index 27ef343afabc..000000000000
--- a/api-gateway/image-microservice/pom.xml
+++ /dev/null
@@ -1,83 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.23.0-SNAPSHOT
-
- 4.0.0
- image-microservice
- jar
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.image.microservice.ImageApplication
-
-
-
-
-
-
-
-
-
diff --git a/api-gateway/image-microservice/src/main/resources/application.properties b/api-gateway/image-microservice/src/main/resources/application.properties
deleted file mode 100644
index da11b3c3db44..000000000000
--- a/api-gateway/image-microservice/src/main/resources/application.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2019 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.
-#
-
-server.port=50005
\ No newline at end of file
diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml
deleted file mode 100644
index 63a98699677c..000000000000
--- a/api-gateway/pom.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
- java-design-patterns
- com.iluwatar
- 1.23.0-SNAPSHOT
-
- 4.0.0
- api-gateway
- pom
-
- image-microservice
- price-microservice
- api-gateway-service
-
-
diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml
deleted file mode 100644
index e7b144c5895c..000000000000
--- a/api-gateway/price-microservice/pom.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.23.0-SNAPSHOT
-
-
- 4.0.0
- price-microservice
- jar
-
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.price.microservices.PriceApplication
-
-
-
-
-
-
-
-
-
diff --git a/api-gateway/price-microservice/src/main/resources/application.properties b/api-gateway/price-microservice/src/main/resources/application.properties
deleted file mode 100644
index 139fa222314e..000000000000
--- a/api-gateway/price-microservice/src/main/resources/application.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2019 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.
-#
-
-server.port=50006
\ No newline at end of file
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
index d1bda1a528cd..519152e1268c 100644
--- a/async-method-invocation/README.md
+++ b/async-method-invocation/README.md
@@ -1,31 +1,217 @@
---
-layout: pattern
-title: Async Method Invocation
-folder: async-method-invocation
-permalink: /patterns/async-method-invocation/
-categories: Concurrency
-tags:
- - Reactive
+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
---
-## Intent
-Asynchronous method invocation is pattern where the calling thread
-is not blocked while waiting results of tasks. The pattern provides parallel
-processing of multiple independent tasks and retrieving the results via
-callbacks or waiting until everything is done.
+## Also known as
-# Class diagram
-
+* Asynchronous Procedure Call
-## Applicability
-Use async method invocation pattern when
+## Intent of Async Method Invocation Design Pattern
-* you have multiple independent tasks that can run in parallel
-* you need to improve the performance of a group of sequential tasks
-* you have limited amount of processing capacity or long running tasks and the
- caller should not wait the tasks to be ready
+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.
-## Real world examples
+## Detailed Explanation of Async Method Invocation Pattern with Real-World Examples
-* [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) and [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) (Java)
-* [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx) (.NET)
+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.ucls b/async-method-invocation/etc/async-method-invocation.ucls
deleted file mode 100644
index f2e189eaeae0..000000000000
--- a/async-method-invocation/etc/async-method-invocation.ucls
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
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
index 6f5d0b27f9d4..d44665081856 100644
--- a/async-method-invocation/etc/async-method-invocation.urm.puml
+++ b/async-method-invocation/etc/async-method-invocation.urm.puml
@@ -9,7 +9,8 @@ package com.iluwatar.async.method.invocation {
+ main(args : String[]) {static}
}
interface AsyncCallback {
- + onComplete(T, Optional) {abstract}
+ + onComplete(T) {abstract}
+ + onError(Exception) {abstract}
}
interface AsyncExecutor {
+ endProcess(AsyncResult) : T {abstract}
@@ -32,7 +33,7 @@ package com.iluwatar.async.method.invocation {
~ COMPLETED : int {static}
~ FAILED : int {static}
~ RUNNING : int {static}
- ~ callback : Optional>
+ ~ callback : AsyncCallback
~ exception : Exception
~ lock : Object
~ state : int
@@ -40,12 +41,15 @@ package com.iluwatar.async.method.invocation {
~ CompletableResult(callback : AsyncCallback)
+ await()
+ getValue() : T
+ ~ hasCallback() : boolean
+ isCompleted() : boolean
~ setException(exception : Exception)
~ setValue(value : T)
}
}
+ThreadAsyncExecutor ..|> AsyncCallback
+ThreadAsyncExecutor ..|> AsyncResult
+ThreadAsyncExecutor ..|> AsyncExecutor
CompletableResult ..+ ThreadAsyncExecutor
-ThreadAsyncExecutor ..|> AsyncExecutor
-CompletableResult ..|> AsyncResult
-@enduml
\ No newline at end of file
+CompletableResult ..|> AsyncResult
+@enduml
diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml
index 46aa9d354a0f..d9ddd918c8e9 100644
--- a/async-method-invocation/pom.xml
+++ b/async-method-invocation/pom.xml
@@ -1,64 +1,75 @@
-
-
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.23.0-SNAPSHOT
-
- async-method-invocation
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.mockito
- mockito-core
- test
-
-
- junit
- junit
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.async.method.invocation.App
-
-
-
-
-
-
-
-
+
+
+
+ 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
index 40c186704cd7..ec3beed3be4d 100644
--- 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
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,23 +22,23 @@
* 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
/**
- * This application demonstrates the async method invocation pattern. Key parts of the pattern are
- * AsyncResult
which is an intermediate container for an asynchronously evaluated
+ * 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.
+ *
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
@@ -55,13 +57,12 @@
* @see java.util.concurrent.CompletableFuture
* @see java.util.concurrent.ExecutorService
*/
+@Slf4j
public class App {
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+ private static final String ROCKET_LAUNCH_LOG_PATTERN = "Space rocket <%s> launched successfully";
- /**
- * Program entry point.
- */
+ /** Program entry point. */
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
var executor = new ThreadAsyncExecutor();
@@ -70,13 +71,14 @@ public static void main(String[] args) throws Exception {
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("Callback result 4"));
+ final var asyncResult4 =
+ executor.startProcess(lazyval(20, 400), callback("Deploying lunar rover"));
final var asyncResult5 =
- executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
+ 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 I'm working hard here
- log("Some hard work done");
+ 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);
@@ -86,22 +88,22 @@ public static void main(String[] args) throws Exception {
asyncResult5.await();
// log the results of the tasks, callbacks log immediately when complete
- log("Result 1: " + result1);
- log("Result 2: " + result2);
- log("Result 3: " + result3);
+ 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 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("Task completed with: " + value);
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, value));
return value;
};
}
@@ -113,11 +115,15 @@ private static Callable lazyval(T value, long delayMillis) {
* @return new async callback
*/
private static AsyncCallback callback(String name) {
- return (value, ex) -> {
- if (ex.isPresent()) {
- log(name + " failed: " + ex.map(Exception::getMessage).orElse(""));
- } else {
- log(name + ": " + value);
+ return new AsyncCallback<>() {
+ @Override
+ public void onComplete(T value) {
+ log(name + " <" + value + ">");
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ log(name + " failed: " + ex.getMessage());
}
};
}
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
index 22b36134ff97..2dfbaf9a188a 100644
--- 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
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,11 +22,8 @@
* 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.Optional;
-
/**
* AsyncCallback interface.
*
@@ -33,10 +32,16 @@
public interface AsyncCallback {
/**
- * Complete handler which is executed when async task is completed or fails execution.
+ * 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 value the evaluated value from async task, undefined when execution fails
- * @param ex empty value if execution succeeds, some exception if executions fails
+ * @param ex exception which was thrown during async task execution(non-null)
*/
- void onComplete(T value, Optional ex);
+ 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
index 819ffd2377ca..3bae90830098 100644
--- 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
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,12 @@
* 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.
- */
+/** AsyncExecutor interface. */
public interface AsyncExecutor {
/**
@@ -43,7 +42,7 @@ public interface AsyncExecutor {
* 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 task task to be executed asynchronously
* @param callback callback to be executed on task completion
* @return async result for the task
*/
@@ -55,7 +54,7 @@ public interface AsyncExecutor {
*
* @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 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
index 6aaf233b44f7..3eebdc4e773d 100644
--- 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
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* 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;
@@ -43,7 +44,7 @@ public interface AsyncResult {
* 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 ExecutionException if execution has failed, containing the root cause
* @throws IllegalStateException if execution is not completed
*/
T getValue() throws ExecutionException;
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
index 7bdf841716d2..a1261f34184c 100644
--- 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
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,22 +22,16 @@
* 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.Optional;
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.
- */
+/** Implementation of async executor that creates a new thread for every task. */
public class ThreadAsyncExecutor implements AsyncExecutor {
- /**
- * Index for thread naming.
- */
+ /** Index for thread naming. */
private final AtomicInteger idx = new AtomicInteger(0);
@Override
@@ -46,19 +42,22 @@ public AsyncResult startProcess(Callable task) {
@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();
+ 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 {
+ public T endProcess(AsyncResult asyncResult)
+ throws ExecutionException, InterruptedException {
if (!asyncResult.isCompleted()) {
asyncResult.await();
}
@@ -80,7 +79,7 @@ private static class CompletableResult implements AsyncResult {
static final int COMPLETED = 3;
final Object lock;
- final Optional> callback;
+ final AsyncCallback callback;
volatile int state = RUNNING;
T value;
@@ -88,7 +87,11 @@ private static class CompletableResult implements AsyncResult {
CompletableResult(AsyncCallback callback) {
this.lock = new Object();
- this.callback = Optional.ofNullable(callback);
+ this.callback = callback;
+ }
+
+ boolean hasCallback() {
+ return callback != null;
}
/**
@@ -100,7 +103,9 @@ private static class CompletableResult implements AsyncResult {
void setValue(T value) {
this.value = value;
this.state = COMPLETED;
- this.callback.ifPresent(ac -> ac.onComplete(value, Optional.empty()));
+ if (hasCallback()) {
+ callback.onComplete(value);
+ }
synchronized (lock) {
lock.notifyAll();
}
@@ -115,7 +120,9 @@ void setValue(T value) {
void setException(Exception exception) {
this.exception = exception;
this.state = FAILED;
- this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception)));
+ if (hasCallback()) {
+ callback.onError(exception);
+ }
synchronized (lock) {
lock.notifyAll();
}
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
index 830e66a2d273..d58a3f6b5c30 100644
--- 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
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,17 +22,24 @@
* 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
- */
+/** 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 test() throws Exception {
- App.main(new String[]{});
+ 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
index 1e54747faf99..d6540ce77309 100644
--- 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
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,80 +22,63 @@
* 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.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertTimeout;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-import static org.mockito.Matchers.eq;
+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.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;
-import java.util.Optional;
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.Matchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-/**
- * Date: 12/6/15 - 10:49 AM
- *
- * @author Jeroen Meulemeester
- */
+/** ThreadAsyncExecutorTest */
class ThreadAsyncExecutorTest {
- @Captor
- private ArgumentCaptor> optionalCaptor;
+ @Captor private ArgumentCaptor exceptionCaptor;
- @Mock
- private Callable task;
+ @Mock private Callable task;
- @Mock
- private AsyncCallback callback;
+ @Mock private AsyncCallback callback;
@BeforeEach
void setUp() {
- MockitoAnnotations.initMocks(this);
+ MockitoAnnotations.openMocks(this);
}
- /**
- * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)}
- */
+ /** Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} */
@Test
- void testSuccessfulTaskWithoutCallback() throws Exception {
- 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());
- });
+ 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());
+ });
}
/**
@@ -101,32 +86,31 @@ void testSuccessfulTaskWithoutCallback() throws Exception {
* AsyncCallback)}
*/
@Test
- void testSuccessfulTaskWithCallback() throws Exception {
- 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), optionalCaptor.capture());
-
- final var optionalException = optionalCaptor.getValue();
- assertNotNull(optionalException);
- assertFalse(optionalException.isPresent());
-
- // ... and the result should be exactly the same object
- assertSame(result, asyncResult.getValue());
- });
+ 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());
+ });
}
/**
@@ -134,39 +118,44 @@ void testSuccessfulTaskWithCallback() throws Exception {
* task takes a while to execute
*/
@Test
- void testLongRunningTaskWithoutCallback() throws Exception {
- 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());
- });
+ 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());
+ });
}
/**
@@ -174,46 +163,48 @@ void testLongRunningTaskWithoutCallback() throws Exception {
* AsyncCallback)} when a task takes a while to execute
*/
@Test
- void testLongRunningTaskWithCallback() throws Exception {
- 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());
-
- verifyZeroInteractions(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), optionalCaptor.capture());
-
- final var optionalException = optionalCaptor.getValue();
- assertNotNull(optionalException);
- assertFalse(optionalException.isPresent());
-
- // 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());
- });
+ 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());
+ });
}
/**
@@ -222,36 +213,41 @@ void testLongRunningTaskWithCallback() throws Exception {
* ThreadAsyncExecutor#endProcess(AsyncResult)}
*/
@Test
- void testEndProcess() throws Exception {
- 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);
- });
+ 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);
+ });
}
/**
@@ -259,26 +255,29 @@ void testEndProcess() throws Exception {
* the callable is 'null'
*/
@Test
- void testNullTask() throws Exception {
- 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());
- }
- });
-
+ 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());
+ }
+ });
}
/**
@@ -286,35 +285,36 @@ void testNullTask() throws Exception {
* AsyncCallback)} when the callable is 'null', but the asynchronous callback is provided
*/
@Test
- void testNullTaskWithCallback() throws Exception {
- 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(1)).onComplete(Matchers.isNull(), optionalCaptor.capture());
-
- final var optionalException = optionalCaptor.getValue();
- assertNotNull(optionalException);
- assertTrue(optionalException.isPresent());
-
- final var exception = optionalException.get();
- 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());
- }
- });
-
+ 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());
+ }
+ });
}
/**
@@ -322,29 +322,28 @@ void testNullTaskWithCallback() throws Exception {
* AsyncCallback)} when both the callable and the asynchronous callback are 'null'
*/
@Test
- void testNullTaskWithNullCallback() throws Exception {
- 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());
- }
- });
-
+ 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());
+ }
+ });
}
-
-}
\ No newline at end of file
+}
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
index 22257ac7bb52..cdd09cf5b7d1 100644
--- a/balking/README.md
+++ b/balking/README.md
@@ -1,27 +1,161 @@
---
-layout: pattern
-title: Balking
-folder: balking
-permalink: /patterns/balking/
-categories: Concurrency
-tags:
- - Decoupling
+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
-Balking Pattern is used to prevent an object from executing certain code if it is an
-incomplete or inappropriate state
+## Intent of Balking Design Pattern
-## Class diagram
-
+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
-## Applicability
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
+* 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
-## Related patterns
-* Guarded Suspension Pattern
-* Double Checked Locking Pattern
+* [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/pom.xml b/balking/pom.xml
index 96453169265b..818f7549c330 100644
--- a/balking/pom.xml
+++ b/balking/pom.xml
@@ -1,54 +1,70 @@
-
-
-
- java-design-patterns
- com.iluwatar
- 1.23.0-SNAPSHOT
-
- 4.0.0
+
+
+
+ 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
index 3e72acc59efc..c7651ec54147 100644
--- a/balking/src/main/java/com/iluwatar/balking/App.java
+++ b/balking/src/main/java/com/iluwatar/balking/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,31 +22,26 @@
* 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.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+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 WashingMachine is an object that has two states in which it
- * can be: ENABLED and WASHING. If the machine is ENABLED the state is changed into WASHING that any
- * other thread can't invoke this action on this and then do the job. On the other hand if it have
- * been already washing and any other thread execute wash() it can't do that once again and returns
- * doing nothing.
+ *
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 {
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
-
/**
* Entry Point.
*
@@ -58,10 +55,12 @@ public static void main(String... args) {
}
executorService.shutdown();
try {
- executorService.awaitTermination(10, TimeUnit.SECONDS);
+ 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
index ed05cd292f6f..f27922219ea6 100644
--- a/balking/src/main/java/com/iluwatar/balking/DelayProvider.java
+++ b/balking/src/main/java/com/iluwatar/balking/DelayProvider.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,14 +22,11 @@
* 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.
- */
+/** 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
index b35bd99dfe61..52ce7c593b6b 100644
--- a/balking/src/main/java/com/iluwatar/balking/WashingMachine.java
+++ b/balking/src/main/java/com/iluwatar/balking/WashingMachine.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,34 +22,32 @@
* 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
-/**
- * Washing machine class.
- */
+/** Washing machine class. */
+@Slf4j
public class WashingMachine {
- private static final Logger LOGGER = LoggerFactory.getLogger(WashingMachine.class);
private final DelayProvider delayProvider;
- private WashingMachineState washingMachineState;
- /**
- * Creates a new instance of WashingMachine.
- */
+ @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) {
- ie.printStackTrace();
- }
- task.run();
- });
+ this(
+ (interval, timeUnit, task) -> {
+ try {
+ Thread.sleep(timeUnit.toMillis(interval));
+ } catch (InterruptedException ie) {
+ LOGGER.error("", ie);
+ Thread.currentThread().interrupt();
+ }
+ task.run();
+ });
}
/**
@@ -59,19 +59,13 @@ public WashingMachine(DelayProvider delayProvider) {
this.washingMachineState = WashingMachineState.ENABLED;
}
- public WashingMachineState getWashingMachineState() {
- return washingMachineState;
- }
-
- /**
- * Method responsible for washing if the object is in appropriate state.
- */
+ /** 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("ERROR: Cannot wash if the machine has been already washing!");
+ LOGGER.error("Cannot wash if the machine has been already washing!");
return;
}
this.washingMachineState = WashingMachineState.WASHING;
@@ -81,12 +75,9 @@ public void wash() {
this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
}
- /**
- * Method responsible of ending the washing by changing machine state.
- */
+ /** 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
index 664a4c0c9941..e0b2ba7ac8f1 100644
--- a/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java
+++ b/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.balking;
/**
@@ -28,5 +29,6 @@
* as well as during washing.
*/
public enum WashingMachineState {
- ENABLED, WASHING
+ ENABLED,
+ WASHING
}
diff --git a/balking/src/test/java/com/iluwatar/balking/AppTest.java b/balking/src/test/java/com/iluwatar/balking/AppTest.java
index 8c75a1f62f69..40beabf553d0 100644
--- a/balking/src/test/java/com/iluwatar/balking/AppTest.java
+++ b/balking/src/test/java/com/iluwatar/balking/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,19 +22,24 @@
* 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
- */
+/** 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 main() {
- App.main();
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow((Executable) App::main);
}
-
-}
\ No newline at end of file
+}
diff --git a/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java b/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
index 9e218e3f0b76..9bf7ac2548a0 100644
--- a/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
+++ b/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* 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;
@@ -28,9 +29,7 @@
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
-/**
- * Tests for {@link WashingMachine}
- */
+/** Tests for {@link WashingMachine} */
class WashingMachineTest {
private final FakeDelayProvider fakeDelayProvider = new FakeDelayProvider();
@@ -68,4 +67,4 @@ public void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task) {
this.task = task;
}
}
-}
\ No newline at end of file
+}
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
index ebd6e5b41e20..26567b292c81 100644
--- a/bridge/README.md
+++ b/bridge/README.md
@@ -1,197 +1,256 @@
---
-layout: pattern
-title: Bridge
-folder: bridge
-permalink: /patterns/bridge/
-categories: Structural
-tags:
- - Gang of Four
+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
-Decouple an abstraction from its implementation so that the two can vary independently.
+* Handle/Body
-## Explanation
+## Intent of Bridge Design Pattern
-Real world example
+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.
-> Consider you have a weapon with different enchantments and you are supposed to allow mixing different weapons with different enchantments. What would you do? Create multiple copies of each of the weapons for each of the enchantments or would you just create separate enchantment and set it for the weapon as needed? Bridge pattern allows you to do the second.
+## 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 over inheritance. Implementation details are pushed from a hierarchy to another object with a separate hierarchy.
+> 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"
-**Programmatic Example**
+Sequence diagram
+
+
+
+## Programmatic Example of Bridge Pattern in Java
-Translating our weapon example from above. Here we have the `Weapon` hierarchy
+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();
+ 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 swinged.");
- enchantment.apply();
- }
-
- @Override
- public void unwield() {
- LOGGER.info("The sword is unwielded.");
- enchantment.onDeactivate();
- }
-
- @Override
- public Enchantment getEnchantment() {
- return enchantment;
- }
+ 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 swinged.");
- enchantment.apply();
- }
-
- @Override
- public void unwield() {
- LOGGER.info("The hammer is unwielded.");
- enchantment.onDeactivate();
- }
-
- @Override
- public Enchantment getEnchantment() {
- return enchantment;
- }
+ 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;
+ }
}
```
-And the separate enchantment hierarchy
+Here's the separate `Enchantment` hierarchy:
```java
public interface Enchantment {
- void onActivate();
- void apply();
- void onDeactivate();
+ 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 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 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.");
- }
+ @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 onActivate() {
+ LOGGER.info("The item spreads bloodlust.");
+ }
- @Override
- public void apply() {
- LOGGER.info("The item eats the soul of enemies.");
- }
+ @Override
+ public void apply() {
+ LOGGER.info("The item eats the soul of enemies.");
+ }
- @Override
- public void onDeactivate() {
- LOGGER.info("Bloodlust slowly disappears.");
- }
+ @Override
+ public void onDeactivate() {
+ LOGGER.info("Bloodlust slowly disappears.");
+ }
}
```
-And both the hierarchies in action
+Here are both hierarchies in action:
```java
-var enchantedSword = new Sword(new SoulEatingEnchantment());
-enchantedSword.wield();
-enchantedSword.swing();
-enchantedSword.unwield();
-// The sword is wielded.
-// The item spreads bloodlust.
-// The sword is swinged.
-// The item eats the soul of enemies.
-// The sword is unwielded.
-// Bloodlust slowly disappears.
-
-var hammer = new Hammer(new FlyingEnchantment());
-hammer.wield();
-hammer.swing();
-hammer.unwield();
-// The hammer is wielded.
-// The item begins to glow faintly.
-// The hammer is swinged.
-// The item flies and strikes the enemies finally returning to owner's hand.
-// The hammer is unwielded.
-// The item's glow fades.
+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();
+}
```
-## Class diagram
-
+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:
-## Applicability
-Use the Bridge pattern when
+* 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.
-* you want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time.
-* both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently
-* changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled.
-* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies
-* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
+## Related Java Design Patterns
-## Tutorial
-* [Bridge Pattern Tutorial](https://www.journaldev.com/1491/bridge-design-pattern-java)
+* [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.
-## Credits
+## References and Credits
-* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
+* [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/pom.xml b/bridge/pom.xml
index 0664bc9b5977..3cfd33997c05 100644
--- a/bridge/pom.xml
+++ b/bridge/pom.xml
@@ -1,43 +1,59 @@
-
-
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.23.0-SNAPSHOT
-
- bridge
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.mockito
- mockito-core
- test
-
-
-
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ bridge
+
+
+ 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
diff --git a/bridge/src/main/java/com/iluwatar/bridge/App.java b/bridge/src/main/java/com/iluwatar/bridge/App.java
index 3e89ef6f67ba..c3ea3d50c35a 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/App.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,11 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bridge;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
/**
* Composition over inheritance. The Bridge pattern can also be thought of as two layers of
@@ -35,14 +35,13 @@
* 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.
+ *
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 {
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
-
/**
* Program entry point.
*
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java b/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java
index 8388fe91eb2b..4bdd4502fd47 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bridge;
-/**
- * Enchantment.
- */
+/** Enchantment. */
public interface Enchantment {
void onActivate();
diff --git a/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java
index 772456b88881..42da3523fb31 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,19 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bridge;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
-/**
- * FlyingEnchantment.
- */
+/** FlyingEnchantment. */
+@Slf4j
public class FlyingEnchantment implements Enchantment {
- private static final Logger LOGGER = LoggerFactory.getLogger(FlyingEnchantment.class);
-
@Override
public void onActivate() {
LOGGER.info("The item begins to glow faintly.");
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Hammer.java b/bridge/src/main/java/com/iluwatar/bridge/Hammer.java
index ffab542cb169..328f3b79e9d6 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/Hammer.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/Hammer.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,25 +22,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bridge;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
-/**
- * Hammer.
- */
+/** Hammer. */
+@Slf4j
+@AllArgsConstructor
public class Hammer implements Weapon {
- private static final Logger LOGGER = LoggerFactory.getLogger(Hammer.class);
-
private final Enchantment enchantment;
- public Hammer(Enchantment enchantment) {
- this.enchantment = enchantment;
- }
-
@Override
public void wield() {
LOGGER.info("The hammer is wielded.");
@@ -47,7 +42,7 @@ public void wield() {
@Override
public void swing() {
- LOGGER.info("The hammer is swinged.");
+ LOGGER.info("The hammer is swung.");
enchantment.apply();
}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java
index ede98d2cbd08..ed22174673d3 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,19 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bridge;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
-/**
- * SoulEatingEnchantment.
- */
+/** SoulEatingEnchantment. */
+@Slf4j
public class SoulEatingEnchantment implements Enchantment {
- private static final Logger LOGGER = LoggerFactory.getLogger(SoulEatingEnchantment.class);
-
@Override
public void onActivate() {
LOGGER.info("The item spreads bloodlust.");
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Sword.java b/bridge/src/main/java/com/iluwatar/bridge/Sword.java
index 71f87a55dcd8..417bc9334046 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/Sword.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/Sword.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,25 +22,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bridge;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
-/**
- * Sword.
- */
+/** Sword. */
+@Slf4j
+@AllArgsConstructor
public class Sword implements Weapon {
- private static final Logger LOGGER = LoggerFactory.getLogger(Sword.class);
-
private final Enchantment enchantment;
- public Sword(Enchantment enchantment) {
- this.enchantment = enchantment;
- }
-
@Override
public void wield() {
LOGGER.info("The sword is wielded.");
@@ -47,7 +42,7 @@ public void wield() {
@Override
public void swing() {
- LOGGER.info("The sword is swinged.");
+ LOGGER.info("The sword is swung.");
enchantment.apply();
}
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Weapon.java b/bridge/src/main/java/com/iluwatar/bridge/Weapon.java
index 76272332e76d..a9f0ab4bbf52 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/Weapon.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/Weapon.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bridge;
-/**
- * Weapon.
- */
+/** Weapon. */
public interface Weapon {
void wield();
diff --git a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java
index d3edbb27ce00..d1136fc90f41 100644
--- a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java
+++ b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,17 +22,23 @@
* 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
- */
+/** 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 test() {
- App.main(new String[]{});
+ 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
index b91f9f402763..d8853647cb84 100644
--- a/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java
+++ b/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* 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;
@@ -28,9 +29,7 @@
import org.junit.jupiter.api.Test;
-/**
- * Tests for hammer
- */
+/** Tests for hammer */
class HammerTest extends WeaponTest {
/**
@@ -42,4 +41,4 @@ void testHammer() {
final var hammer = spy(new Hammer(mock(FlyingEnchantment.class)));
testBasicWeaponActions(hammer);
}
-}
\ No newline at end of file
+}
diff --git a/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java b/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java
index 95a6bc3d33ce..b021cd08d00c 100644
--- a/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java
+++ b/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* 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;
@@ -28,9 +29,7 @@
import org.junit.jupiter.api.Test;
-/**
- * Tests for sword
- */
+/** Tests for sword */
class SwordTest extends WeaponTest {
/**
@@ -42,4 +41,4 @@ void testSword() {
final var sword = spy(new Sword(mock(FlyingEnchantment.class)));
testBasicWeaponActions(sword);
}
-}
\ No newline at end of file
+}
diff --git a/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java
index 620e0104b20c..67648ea6391e 100644
--- a/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java
+++ b/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,16 +22,13 @@
* 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
- */
+/** Base class for weapon tests */
abstract class WeaponTest {
/**
@@ -53,6 +52,5 @@ final void testBasicWeaponActions(final Weapon weapon) {
weapon.unwield();
verify(enchantment).onDeactivate();
verifyNoMoreInteractions(enchantment);
-
}
}
diff --git a/builder/README.md b/builder/README.md
index ccfc378d6e36..cf6e3242dd51 100644
--- a/builder/README.md
+++ b/builder/README.md
@@ -1,23 +1,26 @@
---
-layout: pattern
-title: Builder
-folder: builder
-permalink: /patterns/builder/
-categories: Creational
-tags:
- - Gang of Four
+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
-Separate the construction of a complex object from its
-representation so that the same construction process can create different
-representations.
+## Intent of Builder Design Pattern
-## Explanation
+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.
-Real world example
+## Detailed Explanation of Builder Pattern with Real-World Examples
-> Imagine a character generator for a role playing game. The easiest option is to let computer create the character for you. But if you want to select the character details like profession, gender, hair color etc. the character generation becomes a step-by-step process that completes when all the selections are ready.
+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
@@ -25,42 +28,51 @@ In plain words
Wikipedia says
-> The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor anti-pattern.
+> The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor antipattern.
-Having said that let me add a bit about what telescoping constructor anti-pattern is. At one point or the other we have all seen a constructor like below:
+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) {
+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 get out of hand and it might become difficult to understand the arrangement of parameters. Plus this parameter list could keep on growing if you would want to add more options in future. This is called telescoping constructor anti-pattern.
+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**
+## Programmatic Example of Builder Pattern in Java
-The sane alternative is to use the Builder pattern. First of all we have our hero that we want to create
+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;
- }
+ 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;
+ }
}
```
-And then we have the builder
+Then we have the `Builder`:
```java
public static class Builder {
@@ -72,64 +84,121 @@ And then we have the builder
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;
+ 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;
+ this.hairType = hairType;
+ return this;
}
public Builder withHairColor(HairColor hairColor) {
- this.hairColor = hairColor;
- return this;
+ this.hairColor = hairColor;
+ return this;
}
public Builder withArmor(Armor armor) {
- this.armor = armor;
- return this;
+ this.armor = armor;
+ return this;
}
public Builder withWeapon(Weapon weapon) {
- this.weapon = weapon;
- return this;
+ this.weapon = weapon;
+ return this;
}
public Hero build() {
- return new Hero(this);
+ return new Hero(this);
}
- }
+}
```
-And then it can be used as:
+Then it can be used as:
```java
-var mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER).build();
+ 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());
+}
```
-## Class diagram
-
+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
-## Applicability
Use the Builder pattern when
-* 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
+* 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
-## Real world examples
+* [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)
-* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
-* [java.nio.ByteBuffer](http://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html#put-byte-) as well as similar buffers such as FloatBuffer, IntBuffer and so on.
-* [java.lang.StringBuffer](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html#append-boolean-)
+## 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)
-## Credits
+## 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](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)
-* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683)
+* [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/pom.xml b/builder/pom.xml
index dab9c66a7f5b..3677c187d5e6 100644
--- a/builder/pom.xml
+++ b/builder/pom.xml
@@ -1,38 +1,54 @@
-
-
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.23.0-SNAPSHOT
-
- builder
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ builder
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
org.apache.maven.plugins
diff --git a/builder/src/main/java/com/iluwatar/builder/App.java b/builder/src/main/java/com/iluwatar/builder/App.java
index 76e514749285..acec73a48ee1 100644
--- a/builder/src/main/java/com/iluwatar/builder/App.java
+++ b/builder/src/main/java/com/iluwatar/builder/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,16 +22,14 @@
* 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
/**
* The intention of the Builder pattern is to find a solution to the telescoping constructor
- * anti-pattern. The telescoping constructor anti-pattern occurs when the increase of object
+ * 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.
@@ -48,10 +48,9 @@
* 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 {
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
-
/**
* Program entry point.
*
@@ -59,23 +58,27 @@ public class App {
*/
public static void main(String[] args) {
- var mage = new Hero.Builder(Profession.MAGE, "Riobard")
- .withHairColor(HairColor.BLACK)
- .withWeapon(Weapon.DAGGER)
- .build();
+ 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();
+ 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();
+ 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 5a20a0824318..1710f569af5e 100644
--- a/builder/src/main/java/com/iluwatar/builder/Armor.java
+++ b/builder/src/main/java/com/iluwatar/builder/Armor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,22 +22,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.builder;
-/**
- * Armor enumeration.
- */
-public enum Armor {
+import lombok.AllArgsConstructor;
- CLOTHES("clothes"), LEATHER("leather"), CHAIN_MAIL("chain mail"), PLATE_MAIL("plate mail");
+/** Armor enumeration. */
+@AllArgsConstructor
+public enum Armor {
+ CLOTHES("clothes"),
+ LEATHER("leather"),
+ CHAIN_MAIL("chain mail"),
+ PLATE_MAIL("plate mail");
private final String title;
- Armor(String title) {
- this.title = 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 1beccff5e5b8..7f767c98d661 100644
--- a/builder/src/main/java/com/iluwatar/builder/HairColor.java
+++ b/builder/src/main/java/com/iluwatar/builder/HairColor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,19 +22,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.builder;
-/**
- * HairColor enumeration.
- */
+/** HairColor enumeration. */
public enum HairColor {
-
- WHITE, BLOND, RED, BROWN, BLACK;
+ 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 950f02a1b857..7ac31d0fa03e 100644
--- a/builder/src/main/java/com/iluwatar/builder/HairType.java
+++ b/builder/src/main/java/com/iluwatar/builder/HairType.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,23 +22,21 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.builder;
-/**
- * HairType enumeration.
- */
-public enum HairType {
+import lombok.AllArgsConstructor;
- BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY(
- "long curly");
+/** HairType enumeration. */
+@AllArgsConstructor
+public enum HairType {
+ BALD("bald"),
+ SHORT("short"),
+ CURLY("curly"),
+ LONG_STRAIGHT("long straight"),
+ LONG_CURLY("long curly");
private final String title;
- HairType(String title) {
- this.title = 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 86dc11034c5a..a87137e51fe0 100644
--- a/builder/src/main/java/com/iluwatar/builder/Hero.java
+++ b/builder/src/main/java/com/iluwatar/builder/Hero.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,62 +22,32 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.builder;
-/**
- * Hero, the class with many parameters.
- */
-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;
+/** Hero,the record class. */
+public record Hero(
+ Profession profession,
+ String name,
+ HairType hairType,
+ HairColor hairColor,
+ Armor armor,
+ 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;
- }
-
- 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;
+ 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);
+ sb.append("This is a ").append(profession).append(" named ").append(name);
if (hairColor != null || hairType != null) {
sb.append(" with ");
if (hairColor != null) {
@@ -96,9 +68,7 @@ public String toString() {
return sb.toString();
}
- /**
- * The builder class.
- */
+ /** The builder class. */
public static class Builder {
private final Profession profession;
@@ -108,9 +78,7 @@ public static class Builder {
private Armor armor;
private Weapon weapon;
- /**
- * Constructor.
- */
+ /** Constructor. */
public Builder(Profession profession, String name) {
if (profession == null || name == null) {
throw new IllegalArgumentException("profession and name can not be null");
diff --git a/builder/src/main/java/com/iluwatar/builder/Profession.java b/builder/src/main/java/com/iluwatar/builder/Profession.java
index 23b1ac220c92..c1be949840f8 100644
--- a/builder/src/main/java/com/iluwatar/builder/Profession.java
+++ b/builder/src/main/java/com/iluwatar/builder/Profession.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.builder;
-/**
- * Profession enumeration.
- */
+/** Profession enumeration. */
public enum Profession {
-
- WARRIOR, THIEF, MAGE, PRIEST;
+ WARRIOR,
+ THIEF,
+ MAGE,
+ PRIEST;
@Override
public String toString() {
diff --git a/builder/src/main/java/com/iluwatar/builder/Weapon.java b/builder/src/main/java/com/iluwatar/builder/Weapon.java
index 4ca78b95ff20..03a9565d0cbe 100644
--- a/builder/src/main/java/com/iluwatar/builder/Weapon.java
+++ b/builder/src/main/java/com/iluwatar/builder/Weapon.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.builder;
-/**
- * Weapon enumeration.
- */
+/** Weapon enumeration. */
public enum Weapon {
-
- DAGGER, SWORD, AXE, WARHAMMER, BOW;
+ DAGGER,
+ SWORD,
+ AXE,
+ WARHAMMER,
+ BOW;
@Override
public String toString() {
diff --git a/builder/src/test/java/com/iluwatar/builder/AppTest.java b/builder/src/test/java/com/iluwatar/builder/AppTest.java
index 941f62f75392..367d3da1c1ac 100644
--- a/builder/src/test/java/com/iluwatar/builder/AppTest.java
+++ b/builder/src/test/java/com/iluwatar/builder/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,17 +22,23 @@
* 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
- */
+/** 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 test() {
- App.main(new String[]{});
+ 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
index 97c984a70092..5f67a56aa999 100644
--- a/builder/src/test/java/com/iluwatar/builder/HeroTest.java
+++ b/builder/src/test/java/com/iluwatar/builder/HeroTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* 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;
@@ -29,52 +30,41 @@
import org.junit.jupiter.api.Test;
-/**
- * Date: 12/6/15 - 11:01 PM
- *
- * @author Jeroen Meulemeester
- */
+/** HeroTest */
class HeroTest {
- /**
- * Test if we get the expected exception when trying to create a hero without a profession
- */
+ /** 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 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 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();
+ 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.getProfession());
- assertEquals(heroName, hero.getName());
- assertEquals(Armor.CHAIN_MAIL, hero.getArmor());
- assertEquals(Weapon.SWORD, hero.getWeapon());
- assertEquals(HairType.LONG_CURLY, hero.getHairType());
- assertEquals(HairColor.BLOND, hero.getHairColor());
-
+ 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());
}
-
-}
\ No newline at end of file
+}
diff --git a/business-delegate/README.md b/business-delegate/README.md
index 38eb591b8549..9f83e1a1748a 100644
--- a/business-delegate/README.md
+++ b/business-delegate/README.md
@@ -1,29 +1,193 @@
---
-layout: pattern
-title: Business Delegate
-folder: business-delegate
-permalink: /patterns/business-delegate/
-categories: Structural
-tags:
- - Decoupling
+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
---
-## Intent
-The Business Delegate pattern adds an abstraction layer between
-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.
+## Also known as
-## Class diagram
-
+* 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
-## Applicability
Use the Business Delegate pattern when
-* you want loose coupling between presentation and business tiers
-* you want to orchestrate calls to multiple business services
-* you want to encapsulate service lookups and service calls
+* 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.
-## Credits
+## References and Credits
-* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
+* [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.png b/business-delegate/etc/business-delegate.png
deleted file mode 100644
index 928cf934645b..000000000000
Binary files a/business-delegate/etc/business-delegate.png and /dev/null differ
diff --git a/business-delegate/etc/business-delegate.ucls b/business-delegate/etc/business-delegate.ucls
deleted file mode 100644
index 668a6579eb47..000000000000
--- a/business-delegate/etc/business-delegate.ucls
+++ /dev/null
@@ -1,136 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
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
index 40aa2d6f0997..407e3e12d1ea 100644
--- a/business-delegate/etc/business-delegate.urm.puml
+++ b/business-delegate/etc/business-delegate.urm.puml
@@ -5,53 +5,42 @@ package com.iluwatar.business.delegate {
+ main(args : String[]) {static}
}
class BusinessDelegate {
- - businessService : BusinessService
- lookupService : BusinessLookup
- - serviceType : ServiceType
+ BusinessDelegate()
- + doTask()
- + setLookupService(businessLookup : BusinessLookup)
- + setServiceType(serviceType : ServiceType)
+ + playbackMovie(movie : String)
+ + setLookupService(lookupService : BusinessLookup)
}
class BusinessLookup {
- - ejbService : EjbService
- - jmsService : JmsService
+ - netflixService : NetflixService
+ - youTubeService : YouTubeService
+ BusinessLookup()
- + getBusinessService(serviceType : ServiceType) : BusinessService
- + setEjbService(ejbService : EjbService)
- + setJmsService(jmsService : JmsService)
+ + getBusinessService(movie : String) : VideoStreamingService
+ + setNetflixService(netflixService : NetflixService)
+ + setYouTubeService(youTubeService : YouTubeService)
}
- interface BusinessService {
- + doProcessing() {abstract}
- }
- class Client {
+ class MobileClient {
- businessDelegate : BusinessDelegate
- + Client(businessDelegate : BusinessDelegate)
- + doTask()
+ + MobileClient(businessDelegate : BusinessDelegate)
+ + playbackMovie(movie : String)
}
- class EjbService {
+ class NetflixService {
- LOGGER : Logger {static}
- + EjbService()
+ + NetflixService()
+ doProcessing()
}
- class JmsService {
+ interface VideoStreamingService {
+ + doProcessing() {abstract}
+ }
+ class YouTubeService {
- LOGGER : Logger {static}
- + JmsService()
+ + YouTubeService()
+ doProcessing()
}
- enum ServiceType {
- + EJB {static}
- + JMS {static}
- + valueOf(name : String) : ServiceType {static}
- + values() : ServiceType[] {static}
- }
}
-BusinessLookup --> "-ejbService" EjbService
-BusinessDelegate --> "-serviceType" ServiceType
-Client --> "-businessDelegate" BusinessDelegate
-BusinessDelegate --> "-businessService" BusinessService
+BusinessLookup --> "-netflixService" NetflixService
+BusinessLookup --> "-youTubeService" YouTubeService
+MobileClient --> "-businessDelegate" BusinessDelegate
BusinessDelegate --> "-lookupService" BusinessLookup
-BusinessLookup --> "-jmsService" JmsService
-EjbService ..|> BusinessService
-JmsService ..|> BusinessService
+NetflixService ..|> VideoStreamingService
+YouTubeService ..|> VideoStreamingService
@enduml
\ No newline at end of file
diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml
index 26987c73aef1..a5bccb1529e7 100644
--- a/business-delegate/pom.xml
+++ b/business-delegate/pom.xml
@@ -1,59 +1,75 @@
-
-
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.23.0-SNAPSHOT
-
- business-delegate
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.mockito
- mockito-core
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.business.delegate.App
-
-
-
-
-
-
-
-
+
+
+
+ 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
index 115234df7123..c23ed42caecd 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.business.delegate;
/**
@@ -33,9 +34,9 @@
* 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 Client}) utilizes a business delegate (
- * {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
- * service and makes the service call.
+ *
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 {
@@ -46,18 +47,16 @@ public class App {
*/
public static void main(String[] args) {
+ // prepare the objects
var businessDelegate = new BusinessDelegate();
var businessLookup = new BusinessLookup();
- businessLookup.setEjbService(new EjbService());
- businessLookup.setJmsService(new JmsService());
-
+ businessLookup.setNetflixService(new NetflixService());
+ businessLookup.setYouTubeService(new YouTubeService());
businessDelegate.setLookupService(businessLookup);
- businessDelegate.setServiceType(ServiceType.EJB);
-
- var client = new Client(businessDelegate);
- client.doTask();
- businessDelegate.setServiceType(ServiceType.JMS);
- client.doTask();
+ // 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
index c39a2ad39372..81920c857cd8 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,28 +22,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.business.delegate;
-/**
- * BusinessDelegate separates the presentation and business tiers.
- */
+import lombok.Setter;
+
+/** BusinessDelegate separates the presentation and business tiers. */
+@Setter
public class BusinessDelegate {
private BusinessLookup lookupService;
- private BusinessService businessService;
- private ServiceType serviceType;
-
- public void setLookupService(BusinessLookup businessLookup) {
- this.lookupService = businessLookup;
- }
-
- public void setServiceType(ServiceType serviceType) {
- this.serviceType = serviceType;
- }
- public void doTask() {
- businessService = lookupService.getBusinessService(serviceType);
- businessService.doProcessing();
+ 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
index 489caa23c407..81a1b2f18fd0 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,37 +22,30 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.business.delegate;
-/**
- * Class for performing service lookups.
- */
+import java.util.Locale;
+import lombok.Setter;
+
+/** Class for performing service lookups. */
+@Setter
public class BusinessLookup {
- private EjbService ejbService;
+ private NetflixService netflixService;
- private JmsService jmsService;
+ private YouTubeService youTubeService;
/**
- * Gets service instance based on service type.
+ * Gets service instance based on given movie search string.
*
- * @param serviceType Type of service instance to be returned.
+ * @param movie Search string for the movie.
* @return Service instance.
*/
- public BusinessService getBusinessService(ServiceType serviceType) {
- if (serviceType.equals(ServiceType.EJB)) {
- return ejbService;
+ public VideoStreamingService getBusinessService(String movie) {
+ if (movie.toLowerCase(Locale.ROOT).contains("die hard")) {
+ return netflixService;
} else {
- return jmsService;
+ return youTubeService;
}
}
-
- public void setJmsService(JmsService jmsService) {
- this.jmsService = jmsService;
- }
-
- public void setEjbService(EjbService ejbService) {
- this.ejbService = ejbService;
- }
}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java
deleted file mode 100644
index 3094d3f6e95f..000000000000
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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 service implementations.
- */
-public interface BusinessService {
-
- void doProcessing();
-}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java
deleted file mode 100644
index dcf4ce6b29d3..000000000000
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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;
-
-/**
- * Client utilizes BusinessDelegate to call the business tier.
- */
-public class Client {
-
- private BusinessDelegate businessDelegate;
-
- public Client(BusinessDelegate businessDelegate) {
- this.businessDelegate = businessDelegate;
- }
-
- public void doTask() {
- businessDelegate.doTask();
- }
-}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java
deleted file mode 100644
index 6f39abb1ae42..000000000000
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Service EJB implementation.
- */
-public class EjbService implements BusinessService {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(EjbService.class);
-
- @Override
- public void doProcessing() {
- LOGGER.info("EjbService is now processing");
- }
-}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java
deleted file mode 100644
index 2317d783af0f..000000000000
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Service JMS implementation.
- */
-public class JmsService implements BusinessService {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(JmsService.class);
-
- @Override
- public void doProcessing() {
- LOGGER.info("JmsService is now processing");
- }
-}
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/ServiceType.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java
deleted file mode 100644
index 87fd1562da66..000000000000
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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;
-
-/**
- * Enumeration for service types.
- */
-public enum ServiceType {
-
- EJB, JMS;
-}
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
index 48e756acb816..5f862bf6e5ff 100644
--- a/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java
+++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,20 +22,24 @@
* 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;
-import java.io.IOException;
+/** Tests that Business Delegate example runs without errors. */
+class AppTest {
-/**
- * Tests that Business Delegate example runs without errors.
- */
-public 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
- public void test() throws IOException {
- String[] args = {};
- App.main(args);
+ 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
index 10815ad3a26d..4b8e0ee77d5c 100644
--- a/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java
+++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,33 +22,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.business.delegate;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-/**
- * 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.
- */
-public class BusinessDelegateTest {
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
- private EjbService ejbService;
+/** Tests for the {@link BusinessDelegate} */
+class BusinessDelegateTest {
- private JmsService jmsService;
+ private NetflixService netflixService;
- private BusinessLookup businessLookup;
+ private YouTubeService youTubeService;
private BusinessDelegate businessDelegate;
@@ -55,47 +46,41 @@ public class BusinessDelegateTest {
* execution of every test.
*/
@BeforeEach
- public void setup() {
- ejbService = spy(new EjbService());
- jmsService = spy(new JmsService());
+ void setup() {
+ netflixService = spy(new NetflixService());
+ youTubeService = spy(new YouTubeService());
- businessLookup = spy(new BusinessLookup());
- businessLookup.setEjbService(ejbService);
- businessLookup.setJmsService(jmsService);
+ BusinessLookup businessLookup = spy(new BusinessLookup());
+ businessLookup.setNetflixService(netflixService);
+ businessLookup.setYouTubeService(youTubeService);
businessDelegate = spy(new BusinessDelegate());
businessDelegate.setLookupService(businessLookup);
}
/**
- * In this example the client ({@link Client}) utilizes a business delegate (
- * {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
+ * 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
- public void testBusinessDelegate() {
+ void testBusinessDelegate() {
// setup a client object
- var client = new Client(businessDelegate);
-
- // set the service type
- businessDelegate.setServiceType(ServiceType.EJB);
+ var client = new MobileClient(businessDelegate);
// action
- client.doTask();
-
- // verifying that the businessDelegate was used by client during doTask() method.
- verify(businessDelegate).doTask();
- verify(ejbService).doProcessing();
+ client.playbackMovie("Die hard");
- // set the service type
- businessDelegate.setServiceType(ServiceType.JMS);
+ // verifying that the businessDelegate was used by client during playbackMovie() method.
+ verify(businessDelegate).playbackMovie(anyString());
+ verify(netflixService).doProcessing();
// action
- client.doTask();
+ client.playbackMovie("Maradona");
// verifying that the businessDelegate was used by client during doTask() method.
- verify(businessDelegate, times(2)).doTask();
- verify(jmsService).doProcessing();
+ verify(businessDelegate, times(2)).playbackMovie(anyString());
+ verify(youTubeService).doProcessing();
}
}
diff --git a/bytecode/README.md b/bytecode/README.md
index ee3f96ed8ac3..793eecffcc0e 100644
--- a/bytecode/README.md
+++ b/bytecode/README.md
@@ -1,27 +1,270 @@
---
-layout: pattern
-title: Bytecode
-folder: bytecode
-permalink: /patterns/bytecode/
-categories: Behavioral
-tags:
- - Game programming
+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
-Allows to encode behaviour as instructions for virtual machine.
+## Intent of Bytecode Design Pattern
-## Class diagram
-
+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.
-## Applicability
-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:
+## Detailed Explanation of Bytecode Pattern with Real-World Examples
-* 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 example
-## Credits
+> 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.
-* [Game programming patterns](http://gameprogrammingpatterns.com/bytecode.html)
+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.png b/bytecode/etc/bytecode.png
deleted file mode 100644
index 31b6bc6edba6..000000000000
Binary files a/bytecode/etc/bytecode.png and /dev/null differ
diff --git a/bytecode/etc/bytecode.ucls b/bytecode/etc/bytecode.ucls
deleted file mode 100644
index 3ec390458956..000000000000
--- a/bytecode/etc/bytecode.ucls
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bytecode/etc/bytecode.urm.png b/bytecode/etc/bytecode.urm.png
index 82036a78a9b1..51335fa0a4b4 100644
Binary files a/bytecode/etc/bytecode.urm.png and b/bytecode/etc/bytecode.urm.png differ
diff --git a/bytecode/etc/bytecode.urm.puml b/bytecode/etc/bytecode.urm.puml
index d675ae398c6c..224e909efa15 100644
--- a/bytecode/etc/bytecode.urm.puml
+++ b/bytecode/etc/bytecode.urm.puml
@@ -3,7 +3,6 @@ package com.iluwatar.bytecode {
class App {
- LOGGER : Logger {static}
+ App()
- - interpretInstruction(instruction : String, vm : VirtualMachine) {static}
+ main(args : String[]) {static}
}
enum Instruction {
@@ -18,22 +17,25 @@ package com.iluwatar.bytecode {
+ SET_HEALTH {static}
+ SET_WISDOM {static}
+ SPAWN_PARTICLES {static}
- - value : int
+ - 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)
@@ -45,7 +47,7 @@ package com.iluwatar.bytecode {
- numberOfPlayedSounds : int
- numberOfSpawnedParticles : int
- wisdom : int
- + Wizard()
+ + Wizard(health : int, agility : int, wisdom : int, numberOfPlayedSounds : int, numberOfSpawnedParticles : int)
+ getAgility() : int
+ getHealth() : int
+ getNumberOfPlayedSounds() : int
@@ -54,6 +56,8 @@ package com.iluwatar.bytecode {
+ playSound()
+ setAgility(agility : int)
+ setHealth(health : int)
+ + setNumberOfPlayedSounds(numberOfPlayedSounds : int)
+ + setNumberOfSpawnedParticles(numberOfSpawnedParticles : int)
+ setWisdom(wisdom : int)
+ spawnParticles()
}
diff --git a/bytecode/pom.xml b/bytecode/pom.xml
index f6be69ceede7..2fb01fe2f914 100644
--- a/bytecode/pom.xml
+++ b/bytecode/pom.xml
@@ -1,56 +1,70 @@
-
-
-
- java-design-patterns
- com.iluwatar
- 1.23.0-SNAPSHOT
-
- 4.0.0
+
+
+
+ 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
index 04f473ceeac2..9293b5876ef5 100644
--- a/bytecode/src/main/java/com/iluwatar/bytecode/App.java
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,10 @@
* 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
/**
* The intention of Bytecode pattern is to give behavior the flexibility of data by encoding it as
@@ -40,8 +40,16 @@
* 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 Logger LOGGER = LoggerFactory.getLogger(App.class);
+
+ 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.
@@ -50,31 +58,19 @@ public class App {
*/
public static void main(String[] args) {
- var wizard = new Wizard();
- wizard.setHealth(45);
- wizard.setAgility(7);
- wizard.setWisdom(11);
-
- var vm = new VirtualMachine();
- vm.getWizards()[0] = wizard;
-
- interpretInstruction("LITERAL 0", vm);
- interpretInstruction("LITERAL 0", vm);
- interpretInstruction("GET_HEALTH", vm);
- interpretInstruction("LITERAL 0", vm);
- interpretInstruction("GET_AGILITY", vm);
- interpretInstruction("LITERAL 0", vm);
- interpretInstruction("GET_WISDOM ", vm);
- interpretInstruction("ADD", vm);
- interpretInstruction("LITERAL 2", vm);
- interpretInstruction("DIVIDE", vm);
- interpretInstruction("ADD", vm);
- interpretInstruction("SET_HEALTH", vm);
- }
+ var vm = new VirtualMachine(new Wizard(45, 7, 11, 0, 0), new Wizard(36, 18, 8, 0, 0));
- private static void interpretInstruction(String instruction, VirtualMachine vm) {
- vm.execute(InstructionConverterUtil.convertToByteCode(instruction));
- var stack = vm.getStack();
- LOGGER.info(instruction + String.format("%" + (12 - instruction.length()) + "s", "") + stack);
+ 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
index eeaf16846087..25330e73fd78 100644
--- a/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,35 +22,28 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bytecode;
-/**
- * Representation of instructions understandable by virtual machine.
- */
-public enum Instruction {
-
- LITERAL(1),
- SET_HEALTH(2),
- SET_WISDOM(3),
- SET_AGILITY(4),
- PLAY_SOUND(5),
- SPAWN_PARTICLES(6),
- GET_HEALTH(7),
- GET_AGILITY(8),
- GET_WISDOM(9),
- ADD(10),
- DIVIDE(11);
+import lombok.AllArgsConstructor;
+import lombok.Getter;
- private final int value;
-
- Instruction(int value) {
- this.value = value;
- }
+/** 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
- public int getIntValue() {
- return value;
- }
+ private final int intValue;
/**
* Converts integer value to Instruction.
diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java b/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java
index 5afc2fb93469..7f835d402f15 100644
--- a/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,27 +22,32 @@
* 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.
- */
+/** Implementation of virtual machine. */
+@Getter
+@Slf4j
public class VirtualMachine {
- private Stack stack = new Stack<>();
+ private final Stack stack = new Stack<>();
- private Wizard[] wizards = new Wizard[2];
+ private final Wizard[] wizards = new Wizard[2];
- /**
- * Constructor.
- */
+ /** No-args constructor. */
public VirtualMachine() {
- for (var i = 0; i < wizards.length; i++) {
- wizards[i] = new Wizard();
- }
+ 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;
}
/**
@@ -52,66 +59,64 @@ 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.
+ 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:
+ }
+ case SET_AGILITY -> {
var amount = stack.pop();
var wizard = stack.pop();
setAgility(wizard, amount);
- break;
- case SET_WISDOM:
- amount = stack.pop();
- wizard = stack.pop();
+ }
+ case SET_WISDOM -> {
+ var amount = stack.pop();
+ var wizard = stack.pop();
setWisdom(wizard, amount);
- break;
- case SET_HEALTH:
- amount = stack.pop();
- wizard = stack.pop();
+ }
+ case SET_HEALTH -> {
+ var amount = stack.pop();
+ var wizard = stack.pop();
setHealth(wizard, amount);
- break;
- case GET_HEALTH:
- wizard = stack.pop();
+ }
+ case GET_HEALTH -> {
+ var wizard = stack.pop();
stack.push(getHealth(wizard));
- break;
- case GET_AGILITY:
- wizard = stack.pop();
+ }
+ case GET_AGILITY -> {
+ var wizard = stack.pop();
stack.push(getAgility(wizard));
- break;
- case GET_WISDOM:
- wizard = stack.pop();
+ }
+ case GET_WISDOM -> {
+ var wizard = stack.pop();
stack.push(getWisdom(wizard));
- break;
- case ADD:
+ }
+ case ADD -> {
var a = stack.pop();
var b = stack.pop();
stack.push(a + b);
- break;
- case DIVIDE:
- a = stack.pop();
- b = stack.pop();
+ }
+ case DIVIDE -> {
+ var a = stack.pop();
+ var b = stack.pop();
stack.push(b / a);
- break;
- case PLAY_SOUND:
- wizard = stack.pop();
+ }
+ case PLAY_SOUND -> {
+ var wizard = stack.pop();
getWizards()[wizard].playSound();
- break;
- case SPAWN_PARTICLES:
- wizard = stack.pop();
+ }
+ case SPAWN_PARTICLES -> {
+ var wizard = stack.pop();
getWizards()[wizard].spawnParticles();
- break;
- default:
+ }
+ default -> {
throw new IllegalArgumentException("Invalid instruction value");
+ }
}
+ LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack());
}
}
- public Stack getStack() {
- return stack;
- }
-
public void setHealth(int wizard, int amount) {
wizards[wizard].setHealth(amount);
}
@@ -136,7 +141,7 @@ public int getAgility(int wizard) {
return wizards[wizard].getAgility();
}
- public Wizard[] getWizards() {
- return wizards;
+ 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
index 5153969d9145..8b8bfac9f98b 100644
--- a/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,51 +22,29 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bytecode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+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 static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
private int health;
-
private int agility;
private int wisdom;
-
private int numberOfPlayedSounds;
private int numberOfSpawnedParticles;
- public int getHealth() {
- return health;
- }
-
- public void setHealth(int health) {
- this.health = health;
- }
-
- public int getAgility() {
- return agility;
- }
-
- public void setAgility(int agility) {
- this.agility = agility;
- }
-
- public int getWisdom() {
- return wisdom;
- }
-
- public void setWisdom(int wisdom) {
- this.wisdom = wisdom;
- }
-
public void playSound() {
LOGGER.info("Playing sound");
numberOfPlayedSounds++;
@@ -74,12 +54,4 @@ public void spawnParticles() {
LOGGER.info("Spawning particles");
numberOfSpawnedParticles++;
}
-
- public int getNumberOfPlayedSounds() {
- return numberOfPlayedSounds;
- }
-
- public int getNumberOfSpawnedParticles() {
- return 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
index b0baf326e461..d45a2aa55787 100644
--- a/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,14 +22,11 @@
* 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.
- */
+/** Utility class used for instruction validation and conversion. */
public class InstructionConverterUtil {
/**
* Converts instructions represented as String.
@@ -73,6 +72,4 @@ private static boolean isValidInt(String value) {
return false;
}
}
-
-
}
diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java
index 59962d39e54f..72d00eb34fb3 100644
--- a/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java
+++ b/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,18 +22,23 @@
* 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
- */
-public class AppTest {
+/** 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
- public void test() {
- App.main(new String[]{});
+ 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
index 61a316f5a67b..1d9a5539f51b 100644
--- a/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java
+++ b/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,22 +22,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bytecode;
-import org.junit.jupiter.api.Test;
-
import static com.iluwatar.bytecode.Instruction.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
-/**
- * Test for {@Link VirtualMachine}
- */
-public class VirtualMachineTest {
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link VirtualMachine} */
+class VirtualMachineTest {
@Test
- public void testLiteral() {
+ void testLiteral() {
var bytecode = new int[2];
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = 10;
@@ -48,13 +47,13 @@ public void testLiteral() {
}
@Test
- public void testSetHealth() {
+ 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[3] = 50; // health amount
bytecode[4] = SET_HEALTH.getIntValue();
var vm = new VirtualMachine();
@@ -64,13 +63,13 @@ public void testSetHealth() {
}
@Test
- public void testSetAgility() {
+ 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[3] = 50; // agility amount
bytecode[4] = SET_AGILITY.getIntValue();
var vm = new VirtualMachine();
@@ -80,13 +79,13 @@ public void testSetAgility() {
}
@Test
- public void testSetWisdom() {
+ 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[3] = 50; // wisdom amount
bytecode[4] = SET_WISDOM.getIntValue();
var vm = new VirtualMachine();
@@ -96,15 +95,15 @@ public void testSetWisdom() {
}
@Test
- public void testGetHealth() {
+ 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[3] = 50; // health amount
bytecode[4] = SET_HEALTH.getIntValue();
- bytecode[5] = LITERAL.getIntValue();;
+ bytecode[5] = LITERAL.getIntValue();
bytecode[6] = wizardNumber;
bytecode[7] = GET_HEALTH.getIntValue();
@@ -115,7 +114,7 @@ public void testGetHealth() {
}
@Test
- public void testPlaySound() {
+ void testPlaySound() {
var wizardNumber = 0;
var bytecode = new int[3];
bytecode[0] = LITERAL.getIntValue();
@@ -130,7 +129,7 @@ public void testPlaySound() {
}
@Test
- public void testSpawnParticles() {
+ void testSpawnParticles() {
var wizardNumber = 0;
var bytecode = new int[3];
bytecode[0] = LITERAL.getIntValue();
@@ -145,7 +144,7 @@ public void testSpawnParticles() {
}
@Test
- public void testInvalidInstruction() {
+ void testInvalidInstruction() {
var bytecode = new int[1];
bytecode[0] = 999;
var vm = new VirtualMachine();
diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java
index e7438fce16a8..9dadba1eaf21 100644
--- a/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java
+++ b/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,20 +22,17 @@
* 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 com.iluwatar.bytecode.util.InstructionConverterUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-/**
- * Test for {@Link InstructionConverterUtil}
- */
-public class InstructionConverterUtilTest {
+/** Test for {@link InstructionConverterUtil} */
+class InstructionConverterUtilTest {
+
@Test
- public void testEmptyInstruction() {
+ void testEmptyInstruction() {
var instruction = "";
var bytecode = InstructionConverterUtil.convertToByteCode(instruction);
@@ -42,9 +41,10 @@ public void testEmptyInstruction() {
}
@Test
- public void testInstructions() {
- var instructions = "LITERAL 35 SET_HEALTH SET_WISDOM SET_AGILITY PLAY_SOUND"
- + " SPAWN_PARTICLES GET_HEALTH ADD DIVIDE";
+ 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);
@@ -60,5 +60,4 @@ public void testInstructions() {
Assertions.assertEquals(Instruction.ADD.getIntValue(), bytecode[8]);
Assertions.assertEquals(Instruction.DIVIDE.getIntValue(), bytecode[9]);
}
-
}
diff --git a/caching/README.md b/caching/README.md
index 4172cc72ab62..2e9c3761ef8d 100644
--- a/caching/README.md
+++ b/caching/README.md
@@ -1,28 +1,478 @@
---
-layout: pattern
-title: Caching
-folder: caching
-permalink: /patterns/caching/
-categories: Behavioral
-tags:
- - Performance
+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
---
-## Intent
-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.
+## Also known as
-## Class diagram
-
+* Cache
+* Temporary Storage
-## Applicability
-Use the Caching pattern(s) when
+## Intent of Caching Design Pattern
-* Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead.
+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.
-## Credits
+## Detailed Explanation of Caching Pattern with Real-World Examples
-* [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained)
-* [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177)
-* [Cache-Aside](https://msdn.microsoft.com/en-us/library/dn589799.aspx)
+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
index b6ed703ab8b7..5be2dc0ea7f8 100644
Binary files a/caching/etc/caching.png and b/caching/etc/caching.png differ
diff --git a/caching/etc/caching.urm.puml b/caching/etc/caching.urm.puml
index a9dae801eb20..f6f2e4732005 100644
--- a/caching/etc/caching.urm.puml
+++ b/caching/etc/caching.urm.puml
@@ -13,7 +13,7 @@ package com.iluwatar.caching {
- LOGGER : Logger {static}
+ App()
+ main(args : String[]) {static}
- + useCacheAsideStategy()
+ + useCacheAsideStrategy()
+ useReadAndWriteThroughStrategy()
+ useReadThroughAndWriteAroundStrategy()
+ useReadThroughAndWriteBehindStrategy()
@@ -116,4 +116,4 @@ Node --> "-previous" Node
AppManager --> "-cachingPolicy" CachingPolicy
Node --> "-userAccount" UserAccount
CacheStore --> "-cache" LruCache
-@enduml
\ No newline at end of file
+@enduml
diff --git a/caching/pom.xml b/caching/pom.xml
index 79bde5c951b4..3ffce74af493 100644
--- a/caching/pom.xml
+++ b/caching/pom.xml
@@ -1,8 +1,10 @@
-
+
-
+
4.0.0
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.26.0-SNAPSHOT
caching
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
test
- org.mongodb
- mongodb-driver
- 3.0.4
+ org.mockito
+ mockito-core
+ test
org.mongodb
- mongodb-driver-core
- 3.0.4
+ bson
org.mongodb
- bson
- 3.0.4
+ mongodb-driver-legacy
AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager}
- *
+ *
+ * {@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";
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+ /** 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(String[] args) {
- AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
+ 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).
- AppManager.initCacheCapacity(3);
- var app = new App();
+ 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();
- app.useCacheAsideStategy();
+ LOGGER.info(splitLine);
+ app.useCacheAsideStrategy();
+ LOGGER.info(splitLine);
}
/**
- * Read-through and write-through.
+ * 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);
+ 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");
+ appManager.save(userAccount1);
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("001");
+ appManager.find("001");
}
- /**
- * Read-through and write-around.
- */
+ /** Read-through and write-around. */
public void useReadThroughAndWriteAroundStrategy() {
LOGGER.info("# CachingPolicy.AROUND");
- AppManager.initCachingPolicy(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");
+ 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");
+ appManager.save(userAccount2);
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("002");
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("002");
}
- /**
- * Read-through and write-behind.
- */
+ /** Read-through and write-behind. */
public void useReadThroughAndWriteBehindStrategy() {
LOGGER.info("# CachingPolicy.BEHIND");
- AppManager.initCachingPolicy(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());
+ 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());
+ appManager.save(userAccount6);
+ LOGGER.info(appManager.printCacheContent());
+ appManager.find("004");
+ LOGGER.info(appManager.printCacheContent());
}
- /**
- * Cache-Aside.
- */
- public void useCacheAsideStategy() {
+ /** Cache-Aside. */
+ public void useCacheAsideStrategy() {
LOGGER.info("# CachingPolicy.ASIDE");
- AppManager.initCachingPolicy(CachingPolicy.ASIDE);
- LOGGER.info(AppManager.printCacheContent());
+ 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());
+ 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
index aad98abaedfa..c1d21fea33fe 100644
--- a/caching/src/main/java/com/iluwatar/caching/AppManager.java
+++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,11 +22,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.caching;
-import java.text.ParseException;
+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
@@ -33,11 +35,25 @@
* Depending on the chosen caching policy, AppManager will call the appropriate function in the
* CacheStore class.
*/
-public final class AppManager {
+@Slf4j
+public class AppManager {
+ /** Caching Policy. */
+ private CachingPolicy cachingPolicy;
+
+ /** Database Manager. */
+ private final DbManager dbManager;
- private static CachingPolicy cachingPolicy;
+ /** Cache Store. */
+ private final CacheStore cacheStore;
- private AppManager() {
+ /**
+ * Constructor.
+ *
+ * @param newDbManager database manager
+ */
+ public AppManager(final DbManager newDbManager) {
+ this.dbManager = newDbManager;
+ this.cacheStore = new CacheStore(newDbManager);
}
/**
@@ -45,41 +61,35 @@ private AppManager() {
* data storage or a simple Java data structure to (temporarily) store the data/objects during
* runtime.
*/
- public static void initDb(boolean useMongoDb) {
- if (useMongoDb) {
- try {
- DbManager.connect();
- } catch (ParseException e) {
- e.printStackTrace();
- }
- } else {
- DbManager.createVirtualDb();
- }
+ public void initDb() {
+ dbManager.connect();
}
/**
* Initialize caching policy.
+ *
+ * @param policy is a {@link CachingPolicy}
*/
- public static void initCachingPolicy(CachingPolicy policy) {
+ public void initCachingPolicy(final CachingPolicy policy) {
cachingPolicy = policy;
if (cachingPolicy == CachingPolicy.BEHIND) {
- Runtime.getRuntime().addShutdownHook(new Thread(CacheStore::flushCache));
+ Runtime.getRuntime().addShutdownHook(new Thread(cacheStore::flushCache));
}
- CacheStore.clearCache();
- }
-
- public static void initCacheCapacity(int capacity) {
- CacheStore.initCapacity(capacity);
+ cacheStore.clearCache();
}
/**
* Find user account.
+ *
+ * @param userId String
+ * @return {@link UserAccount}
*/
- public static UserAccount find(String userId) {
+ 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);
+ return cacheStore.readThrough(userId);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
- return CacheStore.readThroughWithWriteBackPolicy(userId);
+ return cacheStore.readThroughWithWriteBackPolicy(userId);
} else if (cachingPolicy == CachingPolicy.ASIDE) {
return findAside(userId);
}
@@ -88,41 +98,55 @@ public static UserAccount find(String userId) {
/**
* Save user account.
+ *
+ * @param userAccount {@link UserAccount}
*/
- public static void save(UserAccount userAccount) {
+ public void save(final UserAccount userAccount) {
+ LOGGER.info("Save record!");
if (cachingPolicy == CachingPolicy.THROUGH) {
- CacheStore.writeThrough(userAccount);
+ cacheStore.writeThrough(userAccount);
} else if (cachingPolicy == CachingPolicy.AROUND) {
- CacheStore.writeAround(userAccount);
+ cacheStore.writeAround(userAccount);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
- CacheStore.writeBehind(userAccount);
+ cacheStore.writeBehind(userAccount);
} else if (cachingPolicy == CachingPolicy.ASIDE) {
saveAside(userAccount);
}
}
- public static String printCacheContent() {
- return CacheStore.print();
+ /**
+ * Returns String.
+ *
+ * @return String
+ */
+ public String printCacheContent() {
+ return cacheStore.print();
}
/**
* Cache-Aside save user account helper.
+ *
+ * @param userAccount {@link UserAccount}
*/
- private static void saveAside(UserAccount userAccount) {
- DbManager.updateDb(userAccount);
- CacheStore.invalidate(userAccount.getUserId());
+ 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 static UserAccount findAside(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;
- })
+ 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
index 90a060e55776..b26c52b22159 100644
--- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java
+++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,31 +22,42 @@
* 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
-/**
- * The caching strategies are implemented in this class.
- */
+/** The caching strategies are implemented in this class. */
+@Slf4j
public class CacheStore {
+ /** Cache capacity. */
+ private static final int CAPACITY = 3;
- private static final Logger LOGGER = LoggerFactory.getLogger(CacheStore.class);
+ /** Lru cache see {@link LruCache}. */
+ private LruCache cache;
- private static LruCache cache;
+ /** DbManager. */
+ private final DbManager dbManager;
- private CacheStore() {
+ /**
+ * Cache Store.
+ *
+ * @param dataBaseManager {@link DbManager}
+ */
+ public CacheStore(final DbManager dataBaseManager) {
+ this.dbManager = dataBaseManager;
+ initCapacity(CAPACITY);
}
/**
* Init cache capacity.
+ *
+ * @param capacity int
*/
- public static void initCapacity(int capacity) {
+ public void initCapacity(final int capacity) {
if (cache == null) {
cache = new LruCache(capacity);
} else {
@@ -54,57 +67,68 @@ public static void initCapacity(int capacity) {
/**
* Get user account using read-through cache.
+ *
+ * @param userId {@link String}
+ * @return {@link UserAccount}
*/
- public static UserAccount readThrough(String userId) {
+ public UserAccount readThrough(final String userId) {
if (cache.contains(userId)) {
- LOGGER.info("# Cache Hit!");
+ LOGGER.info("# Found in Cache!");
return cache.get(userId);
}
- LOGGER.info("# Cache Miss!");
- UserAccount userAccount = DbManager.readFromDb(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 static void writeThrough(UserAccount userAccount) {
+ public void writeThrough(final UserAccount userAccount) {
if (cache.contains(userAccount.getUserId())) {
- DbManager.updateDb(userAccount);
+ dbManager.updateDb(userAccount);
} else {
- DbManager.writeToDb(userAccount);
+ dbManager.writeToDb(userAccount);
}
cache.set(userAccount.getUserId(), userAccount);
}
/**
* Get user account using write-around cache.
+ *
+ * @param userAccount {@link UserAccount}
*/
- public static void writeAround(UserAccount userAccount) {
+ public void writeAround(final UserAccount userAccount) {
if (cache.contains(userAccount.getUserId())) {
- DbManager.updateDb(userAccount);
- cache.invalidate(userAccount.getUserId()); // Cache data has been updated -- remove older
+ dbManager.updateDb(userAccount);
+ // Cache data has been updated -- remove older
+ cache.invalidate(userAccount.getUserId());
// version from cache.
} else {
- DbManager.writeToDb(userAccount);
+ dbManager.writeToDb(userAccount);
}
}
/**
* Get user account using read-through cache with write-back policy.
+ *
+ * @param userId {@link String}
+ * @return {@link UserAccount}
*/
- public static UserAccount readThroughWithWriteBackPolicy(String userId) {
+ public UserAccount readThroughWithWriteBackPolicy(final String userId) {
if (cache.contains(userId)) {
- LOGGER.info("# Cache Hit!");
+ LOGGER.info("# Found in cache!");
return cache.get(userId);
}
- LOGGER.info("# Cache Miss!");
- UserAccount userAccount = DbManager.readFromDb(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);
+ dbManager.upsertDb(toBeWrittenToDb);
}
cache.set(userId, userAccount);
return userAccount;
@@ -112,66 +136,75 @@ public static UserAccount readThroughWithWriteBackPolicy(String userId) {
/**
* Set user account.
+ *
+ * @param userAccount {@link UserAccount}
*/
- public static void writeBehind(UserAccount 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);
+ dbManager.upsertDb(toBeWrittenToDb);
}
cache.set(userAccount.getUserId(), userAccount);
}
- /**
- * Clears cache.
- */
- public static void clearCache() {
+ /** Clears cache. */
+ public void clearCache() {
if (cache != null) {
cache.clear();
}
}
- /**
- * Writes remaining content in the cache into the DB.
- */
- public static void flushCache() {
+ /** 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);
+ .forEach(dbManager::updateDb);
+ dbManager.disconnect();
}
/**
* Print user accounts.
+ *
+ * @return {@link String}
*/
- public static String print() {
+ 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", "----\n"));
+ .collect(Collectors.joining("", "\n--CACHE CONTENT--\n", "----"));
}
/**
* Delegate to backing cache store.
+ *
+ * @param userId {@link String}
+ * @return {@link UserAccount}
*/
- public static UserAccount get(String userId) {
+ public UserAccount get(final String userId) {
return cache.get(userId);
}
/**
* Delegate to backing cache store.
+ *
+ * @param userId {@link String}
+ * @param userAccount {@link UserAccount}
*/
- public static void set(String userId, UserAccount userAccount) {
+ public void set(final String userId, final UserAccount userAccount) {
cache.set(userId, userAccount);
}
/**
* Delegate to backing cache store.
+ *
+ * @param userId {@link String}
*/
- public static void invalidate(String userId) {
+ 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
index 6bc6dbd77d3a..0ec07ced7b8f 100644
--- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
+++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,22 +22,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.caching;
-/**
- * Enum class containing the four caching strategies implemented in the pattern.
- */
-public enum CachingPolicy {
- THROUGH("through"), AROUND("around"), BEHIND("behind"), ASIDE("aside");
-
- private String policy;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
- CachingPolicy(String policy) {
- this.policy = policy;
- }
+/** 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");
- public String getPolicy() {
- return policy;
- }
+ /** Policy value. */
+ private final String policy;
}
diff --git a/caching/src/main/java/com/iluwatar/caching/DbManager.java b/caching/src/main/java/com/iluwatar/caching/DbManager.java
deleted file mode 100644
index 3b2b706e71d2..000000000000
--- a/caching/src/main/java/com/iluwatar/caching/DbManager.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.constants.CachingConstants;
-import com.mongodb.MongoClient;
-import com.mongodb.client.MongoDatabase;
-import com.mongodb.client.model.UpdateOptions;
-import java.text.ParseException;
-import java.util.HashMap;
-import java.util.Map;
-import org.bson.Document;
-
-/**
- * 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.
- *
- * Developer/Tester is able to choose whether the application should use MongoDB as its
- * underlying data storage (connect()) or a simple Java data structure to (temporarily) store the
- * data/objects during runtime (createVirtualDB()).
- */
-public final class DbManager {
-
- private static MongoClient mongoClient;
- private static MongoDatabase db;
- private static boolean useMongoDB;
-
- private static Map virtualDB;
-
- private DbManager() {
- }
-
- /**
- * Create DB.
- */
- public static void createVirtualDb() {
- useMongoDB = false;
- virtualDB = new HashMap<>();
- }
-
- /**
- * Connect to DB.
- */
- public static void connect() throws ParseException {
- useMongoDB = true;
- mongoClient = new MongoClient();
- db = mongoClient.getDatabase("test");
- }
-
- /**
- * Read user account from DB.
- */
- public static UserAccount readFromDb(String userId) {
- if (!useMongoDB) {
- if (virtualDB.containsKey(userId)) {
- return virtualDB.get(userId);
- }
- return null;
- }
- if (db == null) {
- try {
- connect();
- } catch (ParseException e) {
- e.printStackTrace();
- }
- }
- var iterable = db
- .getCollection(CachingConstants.USER_ACCOUNT)
- .find(new Document(CachingConstants.USER_ID, userId));
- if (iterable == null) {
- return null;
- }
- Document doc = iterable.first();
- String userName = doc.getString(CachingConstants.USER_NAME);
- String appInfo = doc.getString(CachingConstants.ADD_INFO);
- return new UserAccount(userId, userName, appInfo);
- }
-
- /**
- * Write user account to DB.
- */
- public static void writeToDb(UserAccount userAccount) {
- if (!useMongoDB) {
- virtualDB.put(userAccount.getUserId(), userAccount);
- return;
- }
- if (db == null) {
- try {
- connect();
- } catch (ParseException e) {
- e.printStackTrace();
- }
- }
- db.getCollection(CachingConstants.USER_ACCOUNT).insertOne(
- new Document(CachingConstants.USER_ID, userAccount.getUserId())
- .append(CachingConstants.USER_NAME, userAccount.getUserName())
- .append(CachingConstants.ADD_INFO, userAccount.getAdditionalInfo())
- );
- }
-
- /**
- * Update DB.
- */
- public static void updateDb(UserAccount userAccount) {
- if (!useMongoDB) {
- virtualDB.put(userAccount.getUserId(), userAccount);
- return;
- }
- if (db == null) {
- try {
- connect();
- } catch (ParseException e) {
- e.printStackTrace();
- }
- }
- db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
- new Document(CachingConstants.USER_ID, userAccount.getUserId()),
- new Document("$set", new Document(CachingConstants.USER_NAME, userAccount.getUserName())
- .append(CachingConstants.ADD_INFO, userAccount.getAdditionalInfo())));
- }
-
- /**
- * Insert data into DB if it does not exist. Else, update it.
- */
- public static void upsertDb(UserAccount userAccount) {
- if (!useMongoDB) {
- virtualDB.put(userAccount.getUserId(), userAccount);
- return;
- }
- if (db == null) {
- try {
- connect();
- } catch (ParseException e) {
- e.printStackTrace();
- }
- }
- db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
- new Document(CachingConstants.USER_ID, userAccount.getUserId()),
- new Document("$set",
- new Document(CachingConstants.USER_ID, userAccount.getUserId())
- .append(CachingConstants.USER_NAME, userAccount.getUserName())
- .append(CachingConstants.ADD_INFO, userAccount.getAdditionalInfo())
- ),
- new UpdateOptions().upsert(true)
- );
- }
-}
diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java
index c443b6b1fbbb..9c9107de6f88 100644
--- a/caching/src/main/java/com/iluwatar/caching/LruCache.java
+++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,15 +22,13 @@
* 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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
/**
* Data structure/implementation of the application's cache. The data structure consists of a hash
@@ -37,35 +37,62 @@
* 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;
+ }
+ }
- private static final Logger LOGGER = LoggerFactory.getLogger(LruCache.class);
+ /** Capacity of Cache. */
+ private int capacity;
- class Node {
- String userId;
- UserAccount userAccount;
- Node previous;
- Node next;
+ /** Cache {@link HashMap}. */
+ private Map cache = new HashMap<>();
- public Node(String userId, UserAccount userAccount) {
- this.userId = userId;
- this.userAccount = userAccount;
- }
- }
+ /** Head. */
+ private Node head;
- int capacity;
- Map cache = new HashMap<>();
- Node head;
- Node end;
+ /** End. */
+ private Node end;
- public LruCache(int capacity) {
- this.capacity = capacity;
+ /**
+ * Constructor.
+ *
+ * @param cap Integer.
+ */
+ public LruCache(final int cap) {
+ this.capacity = cap;
}
/**
* Get user account.
+ *
+ * @param userId String
+ * @return {@link UserAccount}
*/
- public UserAccount get(String userId) {
+ public UserAccount get(final String userId) {
if (cache.containsKey(userId)) {
var node = cache.get(userId);
remove(node);
@@ -77,8 +104,10 @@ public UserAccount get(String userId) {
/**
* Remove node from linked list.
+ *
+ * @param node {@link Node}
*/
- public void remove(Node node) {
+ public void remove(final Node node) {
if (node.previous != null) {
node.previous.next = node.next;
} else {
@@ -93,8 +122,10 @@ public void remove(Node node) {
/**
* Move node to the front of the list.
+ *
+ * @param node {@link Node}
*/
- public void setHead(Node node) {
+ public void setHead(final Node node) {
node.next = head;
node.previous = null;
if (head != null) {
@@ -108,8 +139,11 @@ public void setHead(Node node) {
/**
* Set user account.
+ *
+ * @param userAccount {@link UserAccount}
+ * @param userId {@link String}
*/
- public void set(String userId, UserAccount userAccount) {
+ public void set(final String userId, final UserAccount userAccount) {
if (cache.containsKey(userId)) {
var old = cache.get(userId);
old.userAccount = userAccount;
@@ -129,32 +163,48 @@ public void set(String userId, UserAccount userAccount) {
}
}
- public boolean contains(String userId) {
+ /**
+ * 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(String userId) {
+ 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);
+ 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.
- */
+ /** Clear cache. */
public void clear() {
head = null;
end = null;
@@ -163,6 +213,8 @@ public void clear() {
/**
* Returns cache data in list form.
+ *
+ * @return {@link List}
*/
public List getCacheDataInListForm() {
var listOfCacheData = new ArrayList();
@@ -176,10 +228,14 @@ public List getCacheDataInListForm() {
/**
* Set cache capacity.
+ *
+ * @param newCapacity int
*/
- public void setCapacity(int newCapacity) {
+ public void setCapacity(final int newCapacity) {
if (capacity > newCapacity) {
- clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll
+ // 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
index 4c58cfb08a59..561c942ac781 100644
--- a/caching/src/main/java/com/iluwatar/caching/UserAccount.java
+++ b/caching/src/main/java/com/iluwatar/caching/UserAccount.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,52 +22,25 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.caching;
-/**
- * Entity class (stored in cache and DB) used in the application.
- */
+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;
- private String userName;
- private String additionalInfo;
-
- /**
- * Constructor.
- */
- public UserAccount(String userId, String userName, String additionalInfo) {
- this.userId = userId;
- this.userName = userName;
- this.additionalInfo = additionalInfo;
- }
- public String getUserId() {
- return userId;
- }
-
- public void setUserId(String userId) {
- this.userId = userId;
- }
-
- public String getUserName() {
- return userName;
- }
-
- public void setUserName(String userName) {
- this.userName = userName;
- }
-
- public String getAdditionalInfo() {
- return additionalInfo;
- }
-
- public void setAdditionalInfo(String additionalInfo) {
- this.additionalInfo = additionalInfo;
- }
+ /** User Name. */
+ private String userName;
- @Override
- public String toString() {
- return userId + ", " + userName + ", " + additionalInfo;
- }
+ /** 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
index 86d2bd2a7a8f..5e8fc415df4c 100644
--- a/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java
+++ b/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,17 +22,22 @@
* 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 class CachingConstants {
-
+/** 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
index 831cfe493da5..35e01edbc37e 100644
--- a/caching/src/test/java/com/iluwatar/caching/AppTest.java
+++ b/caching/src/test/java/com/iluwatar/caching/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,19 +22,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.caching;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import java.io.IOException;
+import org.junit.jupiter.api.Test;
-/**
- * Tests that Caching example runs without errors.
- */
-public class AppTest {
+/** 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
- public void test() {
- App.main(new String[]{});
+ 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
index 4db4085d468f..d17cff5bd2ef 100644
--- a/caching/src/test/java/com/iluwatar/caching/CachingTest.java
+++ b/caching/src/test/java/com/iluwatar/caching/CachingTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,49 +22,48 @@
* 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
- */
-public class CachingTest {
+/** Application test */
+class CachingTest {
private App app;
- /**
- * Setup of application test includes: initializing DB connection and cache size/capacity.
- */
+ /** Setup of application test includes: initializing DB connection and cache size/capacity. */
@BeforeEach
- public void setUp() {
+ 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).
- AppManager.initDb(false);
- AppManager.initCacheCapacity(3);
- app = new App();
+ app = new App(false);
}
@Test
- public void testReadAndWriteThroughStrategy() {
+ void testReadAndWriteThroughStrategy() {
+ assertNotNull(app);
app.useReadAndWriteThroughStrategy();
}
@Test
- public void testReadThroughAndWriteAroundStrategy() {
+ void testReadThroughAndWriteAroundStrategy() {
+ assertNotNull(app);
app.useReadThroughAndWriteAroundStrategy();
}
@Test
- public void testReadThroughAndWriteBehindStrategy() {
+ void testReadThroughAndWriteBehindStrategy() {
+ assertNotNull(app);
app.useReadThroughAndWriteBehindStrategy();
}
@Test
- public void testCacheAsideStrategy() {
- app.useCacheAsideStategy();
+ 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
index 6942019f3693..923132ac5b6a 100644
--- a/callback/README.md
+++ b/callback/README.md
@@ -1,26 +1,135 @@
---
-layout: pattern
-title: Callback
-folder: callback
-permalink: /patterns/callback/
-categories: Idiom
-tags:
- - Reactive
+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
---
-## Intent
-Callback is a piece of executable code that is passed as an
-argument to other code, which is expected to call back (execute) the argument
-at some convenient time.
+## Also known as
-## Class diagram
-
+* 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
-## Applicability
Use the Callback pattern when
-* when some arbitrary synchronous or asynchronous action must be performed after execution of some defined activity.
+* 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
-## Real world examples
+## References and Credits
-* [CyclicBarrier](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept callback that will be triggered every time when barrier is tripped.
+* [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
index a666a4fdb25a..2d213eda800c 100644
--- a/callback/etc/callback.urm.puml
+++ b/callback/etc/callback.urm.puml
@@ -8,11 +8,6 @@ package com.iluwatar.callback {
interface Callback {
+ call() {abstract}
}
- class LambdasApp {
- - LOGGER : Logger {static}
- - LambdasApp()
- + main(args : String[]) {static}
- }
class SimpleTask {
- LOGGER : Logger {static}
+ SimpleTask()
diff --git a/callback/pom.xml b/callback/pom.xml
index c156527f5dab..772615f457f9 100644
--- a/callback/pom.xml
+++ b/callback/pom.xml
@@ -1,8 +1,10 @@
-
+
-
+
4.0.0
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.26.0-SNAPSHOT
callback
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/callback/src/main/java/com/iluwatar/callback/App.java b/callback/src/main/java/com/iluwatar/callback/App.java
index 64cb3c796178..7b630f8da247 100644
--- a/callback/src/main/java/com/iluwatar/callback/App.java
+++ b/callback/src/main/java/com/iluwatar/callback/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,28 +22,21 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.callback;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import org.slf4j.Logger;
+import lombok.extern.slf4j.Slf4j;
/**
* Callback pattern is more native for functional languages where functions are treated as
* first-class citizens. Prior to Java 8 callbacks can be simulated using simple (alike command)
* interfaces.
*/
+@Slf4j
public final class App {
- private static final Logger LOGGER = getLogger(App.class);
-
- private App() {
- }
+ private App() {}
- /**
- * Program entry point.
- */
+ /** Program entry point. */
public static void main(final String[] args) {
var task = new SimpleTask();
task.executeWith(() -> LOGGER.info("I'm done now."));
diff --git a/callback/src/main/java/com/iluwatar/callback/Callback.java b/callback/src/main/java/com/iluwatar/callback/Callback.java
index ad98e27deefa..7b75b5c71077 100644
--- a/callback/src/main/java/com/iluwatar/callback/Callback.java
+++ b/callback/src/main/java/com/iluwatar/callback/Callback.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.callback;
-/**
- * Callback interface.
- */
+/** Callback interface. */
public interface Callback {
void call();
diff --git a/callback/src/main/java/com/iluwatar/callback/LambdasApp.java b/callback/src/main/java/com/iluwatar/callback/LambdasApp.java
deleted file mode 100644
index d433217ba6b5..000000000000
--- a/callback/src/main/java/com/iluwatar/callback/LambdasApp.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.callback;
-
-import static org.slf4j.LoggerFactory.getLogger;
-
-import org.slf4j.Logger;
-
-/**
- * This example generates the exact same output as {@link App} however the callback has been defined
- * as a Lambdas expression.
- */
-public final class LambdasApp {
-
- private static final Logger LOGGER = getLogger(LambdasApp.class);
-
- private LambdasApp() {
- }
-
- /**
- * Program entry point.
- */
- public static void main(final String[] args) {
- var task = new SimpleTask();
- task.executeWith(() -> LOGGER.info("I'm done now."));
- }
-}
diff --git a/callback/src/main/java/com/iluwatar/callback/SimpleTask.java b/callback/src/main/java/com/iluwatar/callback/SimpleTask.java
index f12448d9683c..bbf060a6fc9f 100644
--- a/callback/src/main/java/com/iluwatar/callback/SimpleTask.java
+++ b/callback/src/main/java/com/iluwatar/callback/SimpleTask.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,23 +22,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.callback;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import org.slf4j.Logger;
+import lombok.extern.slf4j.Slf4j;
-/**
- * Implementation of task that need to be executed.
- */
+/** Implementation of task that need to be executed. */
+@Slf4j
public final class SimpleTask extends Task {
- private static final Logger LOGGER = getLogger(SimpleTask.class);
-
@Override
public void execute() {
- LOGGER.info("Perform some important activity and after call the"
- + " callback method.");
+ LOGGER.info("Perform some important activity and after call the callback method.");
}
}
diff --git a/callback/src/main/java/com/iluwatar/callback/Task.java b/callback/src/main/java/com/iluwatar/callback/Task.java
index 5632dbc4f103..d69697454dff 100644
--- a/callback/src/main/java/com/iluwatar/callback/Task.java
+++ b/callback/src/main/java/com/iluwatar/callback/Task.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,20 +22,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.callback;
import java.util.Optional;
-/**
- * Template-method class for callback hook execution.
- */
+/** Template-method class for callback hook execution. */
public abstract class Task {
- /**
- * Execute with callback.
- */
- final void executeWith(final Callback callback) {
+ /** Execute with callback. */
+ final void executeWith(Callback callback) {
execute();
Optional.ofNullable(callback).ifPresent(Callback::call);
}
diff --git a/callback/src/main/java/com/iluwatar/callback/package-info.java b/callback/src/main/java/com/iluwatar/callback/package-info.java
index 0b86125fd678..496c71e073db 100644
--- a/callback/src/main/java/com/iluwatar/callback/package-info.java
+++ b/callback/src/main/java/com/iluwatar/callback/package-info.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,5 +22,4 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.callback;
diff --git a/callback/src/test/java/com/iluwatar/callback/AppTest.java b/callback/src/test/java/com/iluwatar/callback/AppTest.java
index c1f466dee8ad..ca0e93072f0f 100644
--- a/callback/src/test/java/com/iluwatar/callback/AppTest.java
+++ b/callback/src/test/java/com/iluwatar/callback/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,17 +22,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.callback;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
import org.junit.jupiter.api.Test;
-/**
- * Tests that Callback example runs without errors.
- */
-public class AppTest {
+/** Tests that Callback 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
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
diff --git a/callback/src/test/java/com/iluwatar/callback/CallbackTest.java b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java
index 08a3df0e1a0b..99939d491f4e 100644
--- a/callback/src/test/java/com/iluwatar/callback/CallbackTest.java
+++ b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2019 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.callback;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -30,15 +31,15 @@
/**
* Add a field as a counter. Every time the callback method is called increment this field. Unit
* test checks that the field is being incremented.
- *
- * Could be done with mock objects as well where the call method call is verified.
+ *
+ *
Could be done with mock objects as well where the call method call is verified.
*/
-public class CallbackTest {
+class CallbackTest {
private Integer callingCount = 0;
@Test
- public void test() {
+ void test() {
Callback callback = () -> callingCount++;
var task = new SimpleTask();
@@ -52,6 +53,5 @@ public void test() {
task.executeWith(callback);
assertEquals(Integer.valueOf(2), callingCount, "Callback called twice");
-
}
}
diff --git a/chain-of-responsibility/README.md b/chain-of-responsibility/README.md
new file mode 100644
index 000000000000..454147dcd5d3
--- /dev/null
+++ b/chain-of-responsibility/README.md
@@ -0,0 +1,209 @@
+---
+title: "Chain of Responsibility Pattern in Java: Building Robust Request Handling Mechanisms"
+shortTitle: Chain of Responsibility
+description: "Learn the Chain of Responsibility design pattern in Java with real-world examples, code snippets, and class diagrams. Enhance your coding skills with our detailed explanations."
+category: Behavioral
+language: en
+tag:
+ - Decoupling
+ - Event-driven
+ - Gang of Four
+ - Messaging
+---
+
+## Also known as
+
+* Chain of Command
+* Chain of Objects
+* Responsibility Chain
+
+## Intent of Chain of Responsibility Design Pattern
+
+The Chain of Responsibility pattern in Java is a behavioral design pattern that decouples the sender of a request from its receivers by giving more than one object a chance to handle the request. The receiving objects are chained and the request is passed along the chain until an object handles it.
+
+## Detailed Explanation of Chain of Responsibility Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world example of the Chain of Responsibility pattern in Java is a technical support call center. When implementing this Java design pattern, each level of support represents a handler in the chain. When a customer calls in with an issue, the call is first received by a front-line support representative. If the issue is simple, the representative handles it directly. If the issue is more complex, the representative forwards the call to a second-level support technician. This process continues, with the call being escalated through multiple levels of support until it reaches a specialist who can resolve the problem. Each level of support represents a handler in the chain, and the call is passed along the chain until it finds an appropriate handler, thereby decoupling the request from the specific receiver.
+
+In plain words
+
+> It helps to build a chain of objects. A request enters from one end and keeps going from an object to another until it finds a suitable handler.
+
+Wikipedia says
+
+> In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.
+
+Flowchart
+
+
+
+## Programmatic Example of Chain of Responsibility Pattern
+
+In this Java example, the Orc King gives orders which are processed by a chain of command representing the Chain of Responsibility pattern. Learn how to implement this design pattern in Java with the following code snippet.
+
+The Orc King gives loud orders to his army. The closest one to react is the commander, then an officer, and then a soldier. The commander, officer, and soldier form a chain of responsibility.
+
+First, we have the `Request` class:
+
+```java
+@Getter
+public class Request {
+
+ private final RequestType requestType;
+ private final String requestDescription;
+ private boolean handled;
+
+ public Request(final RequestType requestType, final String requestDescription) {
+ this.requestType = Objects.requireNonNull(requestType);
+ this.requestDescription = Objects.requireNonNull(requestDescription);
+ }
+
+ public void markHandled() {
+ this.handled = true;
+ }
+
+ @Override
+ public String toString() {
+ return getRequestDescription();
+ }
+}
+
+public enum RequestType {
+ DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
+}
+```
+
+Next, we show the `RequestHandler` hierarchy.
+
+```java
+public interface RequestHandler {
+
+ boolean canHandleRequest(Request req);
+
+ int getPriority();
+
+ void handle(Request req);
+
+ String name();
+}
+
+@Slf4j
+public class OrcCommander implements RequestHandler {
+ @Override
+ public boolean canHandleRequest(Request req) {
+ return req.getRequestType() == RequestType.DEFEND_CASTLE;
+ }
+
+ @Override
+ public int getPriority() {
+ return 2;
+ }
+
+ @Override
+ public void handle(Request req) {
+ req.markHandled();
+ LOGGER.info("{} handling request \"{}\"", name(), req);
+ }
+
+ @Override
+ public String name() {
+ return "Orc commander";
+ }
+}
+
+// OrcOfficer and OrcSoldier are defined similarly as OrcCommander ...
+
+```
+
+The `OrcKing` gives the orders and forms the chain.
+
+```java
+public class OrcKing {
+
+ private List handlers;
+
+ public OrcKing() {
+ buildChain();
+ }
+
+ private void buildChain() {
+ handlers = Arrays.asList(new OrcCommander(), new OrcOfficer(), new OrcSoldier());
+ }
+
+ public void makeRequest(Request req) {
+ handlers
+ .stream()
+ .sorted(Comparator.comparing(RequestHandler::getPriority))
+ .filter(handler -> handler.canHandleRequest(req))
+ .findFirst()
+ .ifPresent(handler -> handler.handle(req));
+ }
+}
+```
+
+The chain of responsibility in action.
+
+```java
+ public static void main(String[] args) {
+
+ var king = new OrcKing();
+ king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle"));
+ king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner"));
+ king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax"));
+}
+```
+
+The console output:
+
+```
+Orc commander handling request "defend castle"
+Orc officer handling request "torture prisoner"
+Orc soldier handling request "collect tax"
+```
+
+## When to Use the Chain of Responsibility Pattern in Java
+
+Use Chain of Responsibility when
+
+* More than one object may handle a request, and the handler isn't known a priori. The handler should be ascertained automatically.
+* You want to issue a request to one of several objects without specifying the receiver explicitly.
+* The set of objects that can handle a request should be specified dynamically.
+
+## Real-World Applications of Chain of Responsibility Pattern in Java
+
+* Event bubbling in GUI frameworks where an event might be handled at multiple levels of a UI component hierarchy
+* Middleware frameworks where a request passes through a chain of processing objects
+* Logging frameworks where messages can be passed through a series of loggers, each possibly handling them differently
+* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29)
+* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html)
+* [javax.servlet.Filter#doFilter()](http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-)
+
+## Benefits and Trade-offs of Chain of Responsibility Pattern
+
+Benefits:
+
+* Reduced coupling. The sender of a request does not need to know the concrete handler that will process the request.
+* Increased flexibility in assigning responsibilities to objects. You can add or change responsibilities for handling a request by changing the members and order of the chain.
+* Allows you to set a default handler if no concrete handler can handle the request.
+
+Trade-Offs:
+
+* It can be challenging to debug and understand the flow, especially if the chain is long and complex.
+* The request might end up unhandled if the chain doesn't include a catch-all handler.
+* Performance concerns might arise due to potentially going through several handlers before finding the right one, or not finding it at all.
+
+## Related Java Design Patterns
+
+* [Command](https://java-design-patterns.com/patterns/command/): can be used to encapsulate a request as an object, which might be passed along the chain.
+* [Composite](https://java-design-patterns.com/patterns/composite/): the Chain of Responsibility is often applied in conjunction with the Composite pattern.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): decorators can be chained in a similar manner as responsibilities in the Chain of Responsibility pattern.
+
+## 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)
+* [Pattern-Oriented Software Architecture, Volume 1: A System of Patterns](https://amzn.to/3PAJUg5)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
+* [Pattern languages of program design 3](https://amzn.to/4a4NxTH)
diff --git a/chain-of-responsibility/etc/chain-of-responsibility-flowchart.png b/chain-of-responsibility/etc/chain-of-responsibility-flowchart.png
new file mode 100644
index 000000000000..875507374086
Binary files /dev/null and b/chain-of-responsibility/etc/chain-of-responsibility-flowchart.png differ
diff --git a/chain-of-responsibility/etc/chain-of-responsibility.urm.png b/chain-of-responsibility/etc/chain-of-responsibility.urm.png
new file mode 100644
index 000000000000..af1bd105455b
Binary files /dev/null and b/chain-of-responsibility/etc/chain-of-responsibility.urm.png differ
diff --git a/chain-of-responsibility/etc/chain-of-responsibility.urm.puml b/chain-of-responsibility/etc/chain-of-responsibility.urm.puml
new file mode 100644
index 000000000000..b548cab81847
--- /dev/null
+++ b/chain-of-responsibility/etc/chain-of-responsibility.urm.puml
@@ -0,0 +1,67 @@
+@startuml
+package com.iluwatar.chain {
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ class OrcCommander {
+ - LOGGER : Logger {static}
+ + OrcCommander()
+ + canHandleRequest(req : Request) : boolean
+ + getPriority() : int
+ + handle(req : Request)
+ + name() : String
+ }
+ class OrcKing {
+ - handlers : List
+ + OrcKing()
+ - buildChain()
+ + makeRequest(req : Request)
+ }
+ class OrcOfficer {
+ - LOGGER : Logger {static}
+ + OrcOfficer()
+ + canHandleRequest(req : Request) : boolean
+ + getPriority() : int
+ + handle(req : Request)
+ + name() : String
+ }
+ class OrcSoldier {
+ - LOGGER : Logger {static}
+ + OrcSoldier()
+ + canHandleRequest(req : Request) : boolean
+ + getPriority() : int
+ + handle(req : Request)
+ + name() : String
+ }
+ class Request {
+ - handled : boolean
+ - requestDescription : String
+ - requestType : RequestType
+ + Request(requestType : RequestType, requestDescription : String)
+ + getRequestDescription() : String
+ + getRequestType() : RequestType
+ + isHandled() : boolean
+ + markHandled()
+ + toString() : String
+ }
+ interface RequestHandler {
+ + canHandleRequest(Request) : boolean {abstract}
+ + getPriority() : int {abstract}
+ + handle(Request) {abstract}
+ + name() : String {abstract}
+ }
+ enum RequestType {
+ + COLLECT_TAX {static}
+ + DEFEND_CASTLE {static}
+ + TORTURE_PRISONER {static}
+ + valueOf(name : String) : RequestType {static}
+ + values() : RequestType[] {static}
+ }
+}
+OrcKing --> "-handlers" RequestHandler
+Request --> "-requestType" RequestType
+OrcCommander ..|> RequestHandler
+OrcOfficer ..|> RequestHandler
+OrcSoldier ..|> RequestHandler
+@enduml
\ No newline at end of file
diff --git a/chain-of-responsibility/pom.xml b/chain-of-responsibility/pom.xml
new file mode 100644
index 000000000000..e6a7fb974be7
--- /dev/null
+++ b/chain-of-responsibility/pom.xml
@@ -0,0 +1,70 @@
+
+
+
+