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 97d730826bb8..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-2021 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,23 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractdocument;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Simple App test
- */
+import org.junit.jupiter.api.Test;
+
+/** Simple App test */
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
- * Solution: Inserted assertion to check whether the execution of the main method in {@link App}
- * throws an exception.
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
*/
-
@Test
void shouldExecuteAppWithoutException() {
assertDoesNotThrow(() -> App.main(null));
}
-
}
diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
index a0e129083b23..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-2021 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,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractdocument;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import com.iluwatar.abstractdocument.domain.Car;
import com.iluwatar.abstractdocument.domain.Part;
import com.iluwatar.abstractdocument.domain.enums.Property;
-import org.junit.jupiter.api.Test;
-
import java.util.List;
import java.util.Map;
+import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * Test for Part and Car
- */
+/** Test for Part and Car */
class DomainTest {
private static final String TEST_PART_TYPE = "test-part-type";
@@ -47,11 +45,11 @@ class DomainTest {
@Test
void shouldConstructPart() {
- var partProperties = Map.of(
- Property.TYPE.toString(), TEST_PART_TYPE,
- Property.MODEL.toString(), TEST_PART_MODEL,
- Property.PRICE.toString(), (Object) TEST_PART_PRICE
- );
+ var 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());
@@ -60,15 +58,14 @@ void shouldConstructPart() {
@Test
void shouldConstructCar() {
- var carProperties = Map.of(
- Property.MODEL.toString(), TEST_CAR_MODEL,
- Property.PRICE.toString(), TEST_CAR_PRICE,
- Property.PARTS.toString(), List.of(Map.of(), Map.of())
- );
+ var 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 4cdbd850a483..ee9823a39a5f 100644
--- a/abstract-factory/README.md
+++ b/abstract-factory/README.md
@@ -1,27 +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
+* Kit
-## Intent
+## Intent of Abstract Factory Design Pattern
-Provide an interface for creating families of related or dependent
-objects without specifying their concrete classes.
+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.
-## Explanation
+## Detailed Explanation of Abstract Factory Pattern with Real-World Examples
-Real world example
+Real-world example
-> To create a kingdom we need objects with a 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.
+> 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
@@ -31,192 +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
-Translating the kingdom example above. First of all we have some interfaces and implementation for the objects in the
-kingdom.
+
+
+## Programmatic Example of Abstract Factory in Java
+
+To create a kingdom using the Abstract Factory pattern in Java, we need objects with a common theme. The elven kingdom needs an elven king, elven castle, and elven army whereas the orcish kingdom needs an orcish king, orcish castle, and orcish army. There is a dependency between the objects in the kingdom.
+
+Translating the kingdom example above. First of all, we have some interfaces and implementation for the objects in the kingdom.
```java
public interface Castle {
- String getDescription();
+ 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!";
+
+ @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();
-public class ElfKingdomFactory implements KingdomFactory {
- public Castle createCastle() {
- return new ElfCastle();
- }
- public King createKing() {
- return new ElfKing();
- }
- public Army createArmy() {
- return new ElfArmy();
- }
-}
+ King createKing();
-public class OrcKingdomFactory implements KingdomFactory {
- public Castle createCastle() {
- return new OrcCastle();
- }
- public King createKing() {
- return new OrcKing();
- }
- public Army createArmy() {
- return new OrcArmy();
- }
+ Army createArmy();
}
-```
-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.
+public class ElfKingdomFactory implements KingdomFactory {
-```java
-var factory = new ElfKingdomFactory();
-var castle = factory.createCastle();
-var king = factory.createKing();
-var army = factory.createArmy();
-
-castle.getDescription();
-king.getDescription();
-army.getDescription();
-```
+ @Override
+ public Castle createCastle() {
+ return new ElfCastle();
+ }
-Program output:
+ @Override
+ public King createKing() {
+ return new ElfKing();
+ }
-```java
-This is the Elven castle!
-This is the Elven king!
-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:
+
+* The system should be independent of how its products are created, composed, and represented.
+* You need to configure the system with one of multiple families of products.
+* A family of related product objects must be used together, enforcing consistency.
+* You want to provide a class library of products, exposing only their interfaces, not their implementations.
+* The lifetime of dependencies is shorter than the consumer's lifetime.
+* Dependencies need to be constructed using runtime values or parameters.
+* You need to choose which product to use from a family at runtime.
+* Adding new products or families should not require changes to existing code.
-
+## Abstract Factory Pattern Java Tutorials
+* [Abstract Factory Design Pattern in Java (DigitalOcean)](https://www.digitalocean.com/community/tutorials/abstract-factory-design-pattern-in-java)
+* [Abstract Factory(Refactoring Guru)](https://refactoring.guru/design-patterns/abstract-factory)
-## Applicability
+## Benefits and Trade-offs of Abstract Factory Pattern
-Use the Abstract Factory pattern when
+Benefits:
-* The system should be independent of how its products are created, composed and represented
-* The system should be configured with one of multiple families of products
-* The 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.
+* Flexibility: Easily switch between product families without code modifications.
-Example use cases
+* Decoupling: Client code only interacts with abstract interfaces, promoting portability and maintainability.
-* Selecting to call to the appropriate implementation of FileSystemAcmeService or DatabaseAcmeService or NetworkAcmeService at runtime.
-* Unit test case writing becomes much easier
-* UI tools for different OS
+* Reusability: Abstract factories and products facilitate component reuse across projects.
-## Consequences:
+* Maintainability: Changes to individual product families are localized, simplifying updates.
-* 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 becomes more complicated than it should be, since a lot of new interfaces and classes are introduced along with the pattern.
+Trade-offs:
-## Tutorial
+* Complexity: Defining abstract interfaces and concrete factories adds initial overhead.
-* [Abstract Factory Pattern Tutorial](https://www.journaldev.com/1418/abstract-factory-design-pattern-in-java)
+* Indirectness: Client code interacts with products indirectly through factories, potentially reducing transparency.
-## Known uses
+## Real-World Applications of Abstract Factory Pattern in Java
+* Java Swing's `LookAndFeel` classes for providing different look-and-feel options.
+* Various implementations in the Java Abstract Window Toolkit (AWT) for creating different GUI components.
* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
* [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--)
* [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--)
-## Related patterns
+## Related Java Design Patterns
-* [Factory Method](https://java-design-patterns.com/patterns/factory-method/)
-* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/)
+* [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.
-## Credits
+## References and Credits
-* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
-* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
+* [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 3b82189b9123..60fbf72eff54 100644
--- a/abstract-factory/pom.xml
+++ b/abstract-factory/pom.xml
@@ -1,8 +1,10 @@
-
+
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.24.0-SNAPSHOT
-
- abstract-factory
-
-
- 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 d208002a721a..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-2021 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,9 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
+import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
/**
@@ -37,17 +39,14 @@
*
*
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.
*/
@Slf4j
+@Getter
public class App implements Runnable {
private final Kingdom kingdom = new Kingdom();
- public Kingdom getKingdom() {
- return kingdom;
- }
-
/**
* Program entry point.
*
@@ -60,13 +59,13 @@ public static void main(String[] args) {
@Override
public void run() {
- LOGGER.info("Elf Kingdom");
+ 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");
+ LOGGER.info("orc kingdom");
createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
LOGGER.info(kingdom.getArmy().getDescription());
LOGGER.info(kingdom.getCastle().getDescription());
@@ -75,6 +74,7 @@ public void run() {
/**
* Creates kingdom.
+ *
* @param kingdomType type of Kingdom
*/
public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) {
@@ -83,4 +83,4 @@ public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) {
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 0ed5dd8bd526..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-2021 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 2977cc978d42..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-2021 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 ad54943aaf1c..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-2021 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 9c2ca1d4b552..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-2021 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 aa64a489f751..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-2021 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 a727a1778af1..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-2021 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 704c14679baf..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-2021 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
index f78e678755fa..d1f85a6a4812 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.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-2021 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,12 @@
* 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 {
@@ -34,30 +36,21 @@ public class Kingdom {
private Castle castle;
private Army army;
- /**
- * The factory of kingdom factories.
- */
+ /** The factory of kingdom factories. */
public static class FactoryMaker {
- /**
- * Enumeration for the different types of Kingdoms.
- */
+ /** Enumeration for the different types of Kingdoms. */
public enum KingdomType {
- ELF, ORC
+ ELF,
+ ORC
}
- /**
- * The factory method to create KingdomFactory concrete objects.
- */
+ /** 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.");
- }
+ 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 520e36de0509..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-2021 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 fa7bf3889b18..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-2021 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 fea7c1866f1f..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-2021 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 9a47a9d0b759..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-2021 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 eb038ca61bd5..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-2021 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 3136ee29fcf2..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-2021 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,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Test for abstract factory.
- */
+import org.junit.jupiter.api.Test;
+
+/** Tests for abstract factory. */
class AbstractFactoryTest {
private final App app = new App();
@Test
- void king() {
+ void verifyKingCreation() {
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
@@ -51,7 +50,7 @@ void king() {
}
@Test
- void castle() {
+ void verifyCastleCreation() {
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
@@ -66,7 +65,7 @@ void castle() {
}
@Test
- void army() {
+ void verifyArmyCreation() {
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
@@ -81,7 +80,7 @@ void army() {
}
@Test
- void createElfKingdom() {
+ void verifyElfKingdomCreation() {
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
@@ -97,7 +96,7 @@ void createElfKingdom() {
}
@Test
- void createOrcKingdom() {
+ void verifyOrcKingdomCreation() {
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
final var kingdom = app.getKingdom();
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 c5589895d7cf..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-2021 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,24 +22,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.abstractfactory;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * 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.
- */
+import org.junit.jupiter.api.Test;
+
+/** Check whether the execution of the main method in {@link App} throws an exception. */
class AppTest {
-
+
@Test
void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
diff --git a/active-object/README.md b/active-object/README.md
index 6e26a85950c4..a804ce9e632b 100644
--- a/active-object/README.md
+++ b/active-object/README.md
@@ -1,125 +1,222 @@
---
-layout: pattern
-title: Active Object
-folder: active-object
-permalink: /patterns/active-object/
-categories: Concurrency
-tags:
- - Performance
+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
-## Intent
-The active object design pattern decouples method execution from method invocation for objects that each reside in their thread of control. The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests.
+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.
-## Explanation
-
-The class that implements the active object pattern will contain a self-synchronization mechanism without using 'synchronized' methods.
+## Detailed Explanation of Active Object Pattern with Real-World Examples
Real-world example
->The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior.
+> 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
-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.
+
-**Programmatic Example**
+## 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());
+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 and the wastelands.",name());
- }
- }
- );
- }
-
- public String name() {
- return this.name;
- }
+ );
+ 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 execute and invocate methods.
+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:
+For example, the `Orc` class:
```java
public class Orc extends ActiveCreature {
- public Orc(String name) {
- super(name);
- }
-
+ 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:
+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 static void main(String[] args) {
- var app = new App();
- app.run();
- }
-
- @Override
- public void run() {
- ActiveCreature creature;
- try {
- for (int i = 0;i < creatures;i++) {
- creature = new Orc(Orc.class.getSimpleName().toString() + i);
- creature.eat();
- creature.roam();
- }
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- logger.error(e.getMessage());
+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();
}
- Runtime.getRuntime().exit(1);
- }
+
+ @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.
```
-## Class diagram
+## 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
similarity index 100%
rename from active-object/etc/active-object.urm.PNG
rename to active-object/etc/active-object.urm.png
diff --git a/active-object/pom.xml b/active-object/pom.xml
index 3ea22d4e23d6..aa26dbbe2e48 100644
--- a/active-object/pom.xml
+++ b/active-object/pom.xml
@@ -1,8 +1,10 @@
-
+
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.24.0-SNAPSHOT
-
- active-object
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.activeobject.App
-
-
-
-
-
-
-
-
+
+ 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
index 479dc0643716..5a440020c0ac 100644
--- a/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java
+++ b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.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-2021 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,96 +22,94 @@
* 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.
- * @author Noam Greenshtain
- *
- */
+/** 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.
- */
+ /** 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 =
+ 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());
- });
+ requests.put(
+ () -> {
+ logger.info("{} is eating!", name());
+ logger.info("{} has finished eating!", name());
+ });
}
/**
- * Roam in the wastelands.
+ * 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())
- );
+ 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() {
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/App.java b/active-object/src/main/java/com/iluwatar/activeobject/App.java
index d36d1023d2ce..ca3a5526ebb8 100644
--- a/active-object/src/main/java/com/iluwatar/activeobject/App.java
+++ b/active-object/src/main/java/com/iluwatar/activeobject/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-2021 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.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.
- *
+ * 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;
/**
@@ -48,16 +48,16 @@ public class App implements Runnable {
*
* @param args command line arguments.
*/
- public static void main(String[] args) {
+ 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++) {
+ for (int i = 0; i < NUM_CREATURES; i++) {
creatures.add(new Orc(Orc.class.getSimpleName() + i));
creatures.get(i).eat();
creatures.get(i).roam();
@@ -67,7 +67,7 @@ public void run() {
logger.error(e.getMessage());
Thread.currentThread().interrupt();
} finally {
- for (int i = 0;i < NUM_CREATURES;i++) {
+ 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
index 2bce5b2b04a9..30adde034de5 100644
--- a/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
+++ b/active-object/src/main/java/com/iluwatar/activeobject/Orc.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-2021 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,12 @@
* 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.
- * @author Noam Greenshtain
- *
- */
+/** 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
index 8a4296a1f3e6..be79e2fb5527 100644
--- a/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java
+++ b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.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-2021 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.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);
- }
-
+ @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
index c7b223d677c6..559e2a1f58f1 100644
--- a/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java
+++ b/active-object/src/test/java/com/iluwatar/activeobject/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-2021 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,16 @@
* 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[]{}));
- }
+ @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 835a5743e4d8..fb57d5681fd4 100644
--- a/acyclic-visitor/README.md
+++ b/acyclic-visitor/README.md
@@ -1,24 +1,25 @@
---
-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
+## Intent of Acyclic Visitor Design Pattern
-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.
+The Acyclic Visitor pattern in Java decouples operations from an object hierarchy, providing a flexible design for various applications.
-## Explanation
+## Detailed Explanation of Acyclic Visitor Pattern with Real-World Examples
-Real world example
+Real-world example
-> We have a hierarchy of modem classes. The modems in this hierarchy need to be visited by an external algorithm based
-> on filtering criteria (is it Unix or DOS compatible modem).
+> 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
@@ -26,108 +27,123 @@ In plain words
[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.
+> 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.
-**Programmatic Example**
+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 abstract void accept(ModemVisitor modemVisitor);
}
public class Zoom extends Modem {
- ...
- @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");
+
+ // 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 {
- ...
- @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");
+
+ // 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.
+Next, we introduce the `ModemVisitor` hierarchy.
```java
public interface ModemVisitor {
}
public interface HayesVisitor extends ModemVisitor {
- void visit(Hayes hayes);
+ void visit(Hayes hayes);
}
public interface ZoomVisitor extends ModemVisitor {
- void visit(Zoom zoom);
+ void visit(Zoom zoom);
}
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
}
public class ConfigureForDosVisitor implements AllModemVisitor {
- ...
- @Override
- public void visit(Hayes hayes) {
- LOGGER.info(hayes + " used with Dos configurator.");
- }
- @Override
- public void visit(Zoom zoom) {
- LOGGER.info(zoom + " used with Dos configurator.");
- }
+
+ // 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 {
- ...
- @Override
- public void visit(Zoom zoom) {
- LOGGER.info(zoom + " used with Unix configurator.");
- }
+
+ // 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);
- zoom.accept(conDos);
- hayes.accept(conUnix);
- zoom.accept(conUnix);
+
+ 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:
```
- // Hayes modem used with Dos configurator.
- // Zoom modem used with Dos configurator.
- // Only HayesVisitor is allowed to visit Hayes modem
- // Zoom modem used with Unix configurator.
+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.
```
-## Class diagram
-
-
-
-## Applicability
+## When to Use the Acyclic Visitor Pattern in Java
This pattern can be used:
@@ -137,24 +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
+## 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
-The good:
+Benefits:
-* 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.
+* 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.
-The bad:
+Trade-offs:
-* Violates [Liskov's Substitution Principle](https://java-design-patterns.com/principles/#liskov-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.
+* Increased complexity: Can introduce additional complexity with the need for multiple visitor interfaces.
+* Maintenance overhead: Modifying the object hierarchy requires updating all visitors.
-## Related patterns
+## Related Java Design Patterns
-* [Visitor Pattern](https://java-design-patterns.com/patterns/visitor/)
+* [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.
-## Credits
+## References and Credits
-* [Acyclic Visitor by Robert C. Martin](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
-* [Acyclic Visitor in WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
+* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML](https://amzn.to/4bOtzwF)
+* [Acyclic Visitor (Robert C. Martin)](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
+* [Acyclic Visitor (WikiWikiWeb)](https://wiki.c2.com/?AcyclicVisitor)
diff --git a/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png b/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png
new file mode 100644
index 000000000000..a3c2ba56b89f
Binary files /dev/null and b/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png differ
diff --git a/acyclic-visitor/pom.xml b/acyclic-visitor/pom.xml
index 234cbfefc5ba..b4f5646b71c0 100644
--- a/acyclic-visitor/pom.xml
+++ b/acyclic-visitor/pom.xml
@@ -1,7 +1,10 @@
+
-
- 4.0.0
-
-
- com.iluwatar
- java-design-patterns
- 1.24.0-SNAPSHOT
-
-
- acyclic-visitor
-
-
- UTF-8
- 1.8
- 1.8
-
-
-
-
-
- org.assertj
- assertj-core
- 3.9.1
- test
-
-
-
- uk.org.lidalia
- slf4j-test
- 1.2.0
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.mockito
- mockito-all
- 1.10.19
- test
-
-
-
+
+ 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 28345c505315..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-2021 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 b151019c298d..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-2021 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 bfe7f6646b3b..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-2021 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,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
import lombok.extern.slf4j.Slf4j;
/**
- * ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos
- * manufacturer.
+ * ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos manufacturer.
*/
@Slf4j
public class ConfigureForDosVisitor implements AllModemVisitor {
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 1f465967f053..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-2021 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;
import lombok.extern.slf4j.Slf4j;
@@ -36,4 +37,4 @@ public class ConfigureForUnixVisitor implements ZoomVisitor {
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 3e30258f5844..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-2021 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.acyclicvisitor;
import lombok.extern.slf4j.Slf4j;
-/**
- * Hayes class implements its accept method.
- */
+/** Hayes class implements its accept method. */
@Slf4j
-public class Hayes extends Modem {
+public class Hayes implements Modem {
- /**
- * Accepts all visitors but honors only HayesVisitor.
- */
+ /** Accepts all visitors but honors only HayesVisitor. */
@Override
public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof HayesVisitor) {
@@ -41,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 06ee5300df5f..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-2021 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 04018d543135..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-2021 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 b2181fe7c9a9..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-2021 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 01e7e7aca08b..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-2021 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.acyclicvisitor;
import lombok.extern.slf4j.Slf4j;
-/**
- * Zoom class implements its accept method.
- */
+/** Zoom class implements its accept method. */
@Slf4j
-public class Zoom extends Modem {
+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) {
@@ -43,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 e86a64fa6d42..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-2021 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 12f0ba6cedd0..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-2021 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,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Tests that the Acyclic Visitor example runs without errors.
- */
+import org.junit.jupiter.api.Test;
+
+/** Tests that the Acyclic Visitor example runs without errors. */
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
- * Solution: Inserted assertion to check whether the execution of the main method in {@link App}
- * throws an exception.
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
*/
-
@Test
void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
+ 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 b41fc956cd62..000000000000
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitorTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * 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
- */
-class ConfigureForDosVisitorTest {
-
- private final TestLogger logger = TestLoggerFactory.getTestLogger(ConfigureForDosVisitor.class);
-
- @Test
- 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
- 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 d2ea55809aa3..000000000000
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * 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 org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-import uk.org.lidalia.slf4jtest.TestLogger;
-import uk.org.lidalia.slf4jtest.TestLoggerFactory;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.groups.Tuple.tuple;
-import static uk.org.lidalia.slf4jext.Level.INFO;
-
-/**
- * ConfigureForUnixVisitor test class
- */
-class ConfigureForUnixVisitorTest {
-
- private static final TestLogger LOGGER = TestLoggerFactory.getTestLogger(ConfigureForUnixVisitor.class);
-
- @AfterEach
- public void clearLoggers() {
- TestLoggerFactory.clear();
- }
-
- @Test
- 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 eaf9fb152cc2..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-2021 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,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-import org.junit.jupiter.api.Test;
-
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
-/**
- * Hayes test class
- */
+import org.junit.jupiter.api.Test;
+
+/** Hayes test class */
class HayesTest {
@Test
@@ -49,6 +48,6 @@ void testAcceptForUnix() {
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 4373fe818fa5..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-2021 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.acyclicvisitor;
-
-import org.junit.jupiter.api.Test;
-
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-/**
- * Zoom test class
- */
+import org.junit.jupiter.api.Test;
+
+/** Zoom test class */
class ZoomTest {
@Test
diff --git a/adapter/README.md b/adapter/README.md
index 65ffa4fc66c0..489742494709 100644
--- a/adapter/README.md
+++ b/adapter/README.md
@@ -1,27 +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
@@ -31,103 +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
+
+
-Consider a captain that can only use rowing boats and cannot sail at all.
+## Programmatic Example of Adapter Pattern in Java
-First we have interfaces `RowingBoat` and `FishingBoat`
+The Adapter Pattern example in Java shows how a class with an incompatible interface can be adapted to work with another class.
+
+Consider a wannabe captain that can only use rowing boats but can't sail at all.
+
+First, we have interfaces `RowingBoat` and `FishingBoat`
```java
public interface RowingBoat {
- void row();
+ void row();
}
@Slf4j
public class FishingBoat {
- 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 final 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 final 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](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
-* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
-* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
-* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
+* [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 f50a7ae3dd8b..6e7f45a515b5 100644
--- a/adapter/pom.xml
+++ b/adapter/pom.xml
@@ -1,8 +1,10 @@
-
+
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.24.0-SNAPSHOT
-
- adapter
-
-
- 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 1375d85c721a..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-2021 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 036792e8c4c4..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-2021 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 lombok.AllArgsConstructor;
@@ -28,7 +29,8 @@
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
@@ -40,5 +42,4 @@ public final class Captain {
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 38866dc33adc..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-2021 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 lombok.extern.slf4j.Slf4j;
@@ -35,5 +36,4 @@ final class FishingBoat {
void sail() {
LOGGER.info("The fishing boat is sailing");
}
-
}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java
index 73c7dd60eae3..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-2021 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;
/**
diff --git a/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
index 69d39c19352b..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-2021 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 873df29c552d..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-2021 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 fc55cd69e419..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-2021 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.adapter;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import java.util.HashMap;
import java.util.Map;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-/**
- * Test class
- */
+/** Tests for the adapter pattern. */
class AdapterPatternTest {
private Map beans;
@@ -43,11 +41,9 @@ 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());
diff --git a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
index 8b224e451625..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-2021 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,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.adapter;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Tests that Adapter example runs without errors.
- */
-class AppTest {
+import org.junit.jupiter.api.Test;
- /**
- * 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.
- */
+/** Tests that Adapter example runs without errors. */
+class AppTest {
+ /** Check whether the execution of the main method in {@link App} throws an exception. */
@Test
void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md
deleted file mode 100644
index b1bd5d762663..000000000000
--- a/aggregator-microservices/README.md
+++ /dev/null
@@ -1,109 +0,0 @@
----
-layout: pattern
-title: Aggregator Microservices
-folder: aggregator-microservices
-permalink: /patterns/aggregator-microservices/
-categories: Architectural
-tags:
-- Cloud distributed
-- Decoupling
-- Microservices
----
-
-## Intent
-
-The user makes a single call to the aggregator service, and the aggregator then calls each relevant microservice.
-
-## Explanation
-
-Real world example
-
-> Our web marketplace needs information about products and their current inventory. It makes a call to an aggregator
-> service which in turn calls the product information microservice and product inventory microservice returning the
-> combined information.
-
-In plain words
-
-> Aggregator Microservice collects pieces of data from various microservices and returns an aggregate for processing.
-
-Stack Overflow says
-
-> Aggregator Microservice invokes multiple services to achieve the functionality required by the application.
-
-**Programmatic Example**
-
-Let's start from the data model. Here's our `Product`.
-
-```java
-public class Product {
- private String title;
- private int productInventories;
- // getters and setters ->
- ...
-}
-```
-
-Next we can introduce our `Aggregator` microservice. It contains clients `ProductInformationClient` and
-`ProductInventoryClient` for calling respective microservices.
-
-```java
-@RestController
-public class Aggregator {
-
- @Resource
- private ProductInformationClient informationClient;
-
- @Resource
- private ProductInventoryClient inventoryClient;
-
- @RequestMapping(path = "/product", method = RequestMethod.GET)
- public Product getProduct() {
-
- var product = new Product();
- var productTitle = informationClient.getProductTitle();
- var productInventory = inventoryClient.getProductInventories();
-
- //Fallback to error message
- product.setTitle(requireNonNullElse(productTitle, "Error: Fetching Product Title Failed"));
-
- //Fallback to default error inventory
- product.setProductInventories(requireNonNullElse(productInventory, -1));
-
- return product;
- }
-}
-```
-
-Here's the essence of information microservice implementation. Inventory microservice is similar, it just returns
-inventory counts.
-
-```java
-@RestController
-public class InformationController {
- @RequestMapping(value = "/information", method = RequestMethod.GET)
- public String getProductTitle() {
- return "The Product Title.";
- }
-}
-```
-
-Now calling our `Aggregator` REST API returns the product information.
-
-```bash
-curl http://localhost:50004/product
-{"title":"The Product Title.","productInventories":5}
-```
-
-## 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://web.archive.org/web/20190705163602/http://blog.arungupta.me/microservice-design-patterns/)
-* [Microservices Patterns: With examples in Java](https://www.amazon.com/gp/product/1617294543/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617294543&linkId=8b4e570267bc5fb8b8189917b461dc60)
-* [Architectural Patterns: Uncover essential patterns in the most indispensable realm of enterprise architecture](https://www.amazon.com/gp/product/B077T7V8RC/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=B077T7V8RC&linkId=c34d204bfe1b277914b420189f09c1a4)
diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml
deleted file mode 100644
index 46337c0ec351..000000000000
--- a/aggregator-microservices/aggregator-service/pom.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.24.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 791df87ba635..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-2021 Ilkka Seppälä
- *
- * 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 f451728b9e0d..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * 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 lombok.Getter;
-import lombok.Setter;
-
-/**
- * Encapsulates all the data for a Product that clients will request.
- */
-@Getter
-@Setter
-public class Product {
-
- /**
- * The title of the product.
- */
- private String title;
-
-
- /**
- * The inventories of the product.
- */
- private int productInventories;
-
-}
diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml
deleted file mode 100644
index 43d5ba072764..000000000000
--- a/aggregator-microservices/information-microservice/pom.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.24.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/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml
deleted file mode 100644
index 9f74cf7c94a5..000000000000
--- a/aggregator-microservices/inventory-microservice/pom.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.24.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/pom.xml b/aggregator-microservices/pom.xml
deleted file mode 100644
index f5d18c2b4aab..000000000000
--- a/aggregator-microservices/pom.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
- java-design-patterns
- com.iluwatar
- 1.24.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 9010c44b6a93..fa08223feb75 100644
--- a/ambassador/README.md
+++ b/ambassador/README.md
@@ -1,43 +1,49 @@
---
-layout: pattern
-title: Ambassador
-folder: ambassador
-permalink: /patterns/ambassador/
-categories: Structural
-tags:
+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
- - Cloud distributed
+ - Fault tolerance
+ - Proxy
+ - Resilience
+ - Scalability
---
-## Intent
+## Intent of Ambassador Design Pattern
-Provide a helper service instance on a client and offload common functionality away from a shared resource.
+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.
-## Explanation
+## Detailed Explanation of Ambassador Pattern with Real-World Examples
-Real world example
+Real-world example
-> 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.
+> Imagine a busy hotel where guests frequently request restaurant reservations, event tickets, or transportation arrangements. Instead of each guest individually contacting these services, the hotel provides a concierge. The concierge handles these tasks on behalf of the guests, ensuring that reservations are made smoothly, tickets are booked on time, and transportation is scheduled efficiently.
+>
+> In this analogy, the guests are the client services, the external providers (restaurants, ticket vendors, transportation) are the remote services, and the concierge represents the ambassador service. This setup allows the guests to focus on enjoying their stay while the concierge manages the complexities of external interactions, providing a seamless and enhanced experience.
In plain words
-> With the Ambassador pattern, we can implement less-frequent polling from clients along with latency checks and
-> logging.
+> With the Ambassador pattern, we can implement less-frequent polling from clients along with latency checks and logging.
Microsoft documentation states
-> An ambassador service can be thought of as an out-of-process proxy which is co-located with the client. This pattern
-> can be useful for offloading common client connectivity tasks such as monitoring, logging, routing,
-> security (such as TLS), and resiliency patterns in a language agnostic way. It is often used with legacy applications,
-> or other applications that are difficult to modify, in order to extend their networking capabilities. It can also
-> enable a specialized team to implement those features.
+> 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.
-**Programmatic Example**
+Sequence diagram
-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:
+
+
+## Programmatic Example of Ambassador Pattern in Java
+
+In this example of the Ambassador Pattern in Java, we demonstrate how to implement latency checks, logging, and retry mechanisms to improve system reliability.
+
+A remote service has many clients accessing a function it provides. The service is a legacy application and is impossible to update. Large numbers of requests from users are causing connectivity issues. New rules for request frequency should be implemented along with latency checks and client-side logging.
+
+With the above introduction in mind we will imitate the functionality in this example. We have an interface implemented by the remote service as well as the ambassador service.
```java
interface RemoteServiceInterface {
@@ -48,6 +54,7 @@ interface RemoteServiceInterface {
A remote services represented as a singleton.
```java
+
@Slf4j
public class RemoteService implements RemoteServiceInterface {
private static RemoteService service = null;
@@ -59,7 +66,8 @@ public class RemoteService implements RemoteServiceInterface {
return service;
}
- private RemoteService() {}
+ private RemoteService() {
+ }
@Override
public long doRemoteFunction(int value) {
@@ -76,69 +84,71 @@ 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
+
@Slf4j
public class ServiceAmbassador implements RemoteServiceInterface {
- private static final int RETRIES = 3;
- private static final int DELAY_MS = 3000;
-
- ServiceAmbassador() {
- }
-
- @Override
- public long doRemoteFunction(int value) {
- return safeCall(value);
- }
-
- private long checkLatency(int value) {
- var startTime = System.currentTimeMillis();
- var result = RemoteService.getRemoteService().doRemoteFunction(value);
- var timeTaken = System.currentTimeMillis() - startTime;
-
- LOGGER.info("Time taken (ms): " + timeTaken);
- return result;
- }
-
- private long safeCall(int value) {
- var retries = 0;
- var result = (long) FAILURE;
-
- for (int i = 0; i < RETRIES; i++) {
- if (retries >= RETRIES) {
- return FAILURE;
- }
-
- if ((result = checkLatency(value)) == FAILURE) {
- LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
- retries++;
- try {
- sleep(DELAY_MS);
- } catch (InterruptedException e) {
- LOGGER.error("Thread sleep state interrupted", e);
+ private static final int RETRIES = 3;
+ private static final int DELAY_MS = 3000;
+
+ ServiceAmbassador() {
+ }
+
+ @Override
+ public long doRemoteFunction(int value) {
+ return safeCall(value);
+ }
+
+ private long checkLatency(int value) {
+ var startTime = System.currentTimeMillis();
+ var result = RemoteService.getRemoteService().doRemoteFunction(value);
+ var timeTaken = System.currentTimeMillis() - startTime;
+
+ LOGGER.info("Time taken (ms): " + timeTaken);
+ return result;
+ }
+
+ private long safeCall(int value) {
+ var retries = 0;
+ var result = (long) FAILURE;
+
+ for (int i = 0; i < RETRIES; i++) {
+ if (retries >= RETRIES) {
+ return FAILURE;
+ }
+
+ if ((result = checkLatency(value)) == FAILURE) {
+ LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
+ retries++;
+ try {
+ sleep(DELAY_MS);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread sleep state interrupted", e);
+ }
+ } else {
+ break;
+ }
}
- } 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
+
@Slf4j
public class Client {
- private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
+ 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;
+ }
}
```
@@ -146,43 +156,36 @@ 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);
+ }
}
```
Here's the output for running the example:
```java
-Time taken (ms): 111
-Service result: 120
-Time taken (ms): 931
-Failed to reach remote: (1)
-Time taken (ms): 665
-Failed to reach remote: (2)
-Time taken (ms): 538
-Failed to reach remote: (3)
-Service result: -1
+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
```
-## Class diagram
-
-
+## When to Use the Ambassador Pattern in Java
-## Applicability
+* 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.
-Ambassador is applicable when working with a legacy remote service which 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.
-
-* Ambassador provides a local interface for a remote service.
-* Ambassador provides logging, circuit breaking, retries and security on the client.
-
-## Typical Use Case
+Typical use cases include:
* Control access to another object
* Implement logging
@@ -190,15 +193,44 @@ service.
* Offload remote service tasks
* Facilitate network connection
-## Known uses
+## Benefits and Trade-offs of Ambassador Pattern
+
+Benefits:
+
+* Separation of Concerns: Offloads cross-cutting concerns from the service logic, leading to cleaner, more maintainable code.
+* Reusable Infrastructure Logic: The ambassador pattern allows the same logic (e.g., logging, monitoring) to be reused across multiple services.
+* Improved Security: Centralizes security features like SSL termination or authentication, reducing the risk of misconfiguration.
+* Flexibility: Makes it easier to update or replace infrastructure concerns without modifying the service code.
+
+Trade-offs:
+
+* Increased Complexity: Adds another layer to the architecture, which can complicate the system design and debugging.
+* Potential Performance Overhead: The additional network hop can introduce latency and overhead, particularly if not optimized.
+* Deployment Overhead: Requires additional resources and management for deploying and scaling ambassador services.
+
+## Real-World Applications of Ambassador Pattern in Java
+* Service Mesh Implementations: In a service mesh architecture, like Istio or Linkerd, the Ambassador pattern is often employed as a sidecar proxy that handles inter-service communications. This includes tasks such as service discovery, routing, load balancing, telemetry (metrics and tracing), and security (authentication and authorization).
+* API Gateways: API gateways can use the Ambassador pattern to encapsulate common functionalities like rate limiting, caching, request shaping, and authentication. This allows backend services to focus on their core business logic without being burdened by these cross-cutting concerns.
+* Logging and Monitoring: An Ambassador can aggregate logs and metrics from various services and forward them to centralized monitoring tools like Prometheus or ELK Stack (Elasticsearch, Logstash, Kibana). This simplifies the logging and monitoring setup for each service and provides a unified view of the system's health.
+* Security: Security-related functionalities such as SSL/TLS termination, identity verification, and encryption can be managed by an Ambassador. This ensures consistent security practices across services and reduces the likelihood of security breaches due to misconfigurations.
+* Resilience: The Ambassador can implement resilience patterns like circuit breakers, retries, and timeouts. For instance, Netflix's Hystrix library can be used within an Ambassador to prevent cascading failures in a microservices ecosystem.
+* Database Proxy: Ambassadors can act as proxies for database connections, providing functionalities like connection pooling, read/write splitting for replicas, and query caching. This offloads significant complexity from the application services.
+* Legacy System Integration: In scenarios where modern microservices need to communicate with legacy systems, an Ambassador can serve as an intermediary that translates protocols, handles necessary transformations, and implements modern security practices, easing the integration process.
+* Network Optimization: For services deployed across different geographical locations or cloud regions, Ambassadors can optimize communication by compressing data, batching requests, or even implementing smart routing to reduce latency and costs.
* [Kubernetes-native API gateway for microservices](https://github.com/datawire/ambassador)
-## Related patterns
+## Related Java Design Patterns
-* [Proxy](https://java-design-patterns.com/patterns/proxy/)
+* [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.
-## Credits
+## 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 487dab00821f..15e4a07f0dc3 100644
--- a/ambassador/pom.xml
+++ b/ambassador/pom.xml
@@ -1,8 +1,10 @@
-
-
- java-design-patterns
- com.iluwatar
- 1.24.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 6da5a2588135..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-2021 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 0ec258016021..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-2021 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.ambassador;
import lombok.extern.slf4j.Slf4j;
-/**
- * A simple Client.
- */
+/** A simple Client. */
@Slf4j
public class Client {
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java
index 18a2d2214d0b..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-2021 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 java.lang.Thread.sleep;
@@ -28,9 +29,7 @@
import com.iluwatar.ambassador.util.RandomProvider;
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;
@@ -48,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;
}
@@ -74,7 +71,8 @@ public long doRemoteFunction(int value) {
LOGGER.error("Thread sleep state interrupted", e);
Thread.currentThread().interrupt();
}
- return waitTime <= THRESHOLD ? value * 10
+ 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 3012eea29f8a..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-2021 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;
-/**
- * Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}).
- */
+/** Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}). */
interface RemoteServiceInterface {
long doRemoteFunction(int value);
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java
index e7ee06af989f..8549ed7247f3 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.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-2021 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,23 @@
* 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.
+ * 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);
- private final long remoteServiceStatusValue;
+ @Getter private final long remoteServiceStatusValue;
RemoteServiceStatus(long remoteServiceStatusValue) {
this.remoteServiceStatusValue = remoteServiceStatusValue;
}
-
- public long getRemoteServiceStatusValue() {
- return remoteServiceStatusValue;
- }
}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java b/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java
index bd8481d001a9..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-2021 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 com.iluwatar.ambassador.RemoteServiceStatus.FAILURE;
@@ -39,8 +40,7 @@ public class ServiceAmbassador implements RemoteServiceInterface {
private static final int RETRIES = 3;
private static final int DELAY_MS = 3000;
- ServiceAmbassador() {
- }
+ ServiceAmbassador() {}
@Override
public long doRemoteFunction(int value) {
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 9af9da500480..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-2021 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 57658be39ad9..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-2021 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,24 @@
* 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.assertDoesNotThrow;
-/**
- * Application test
- */
+import org.junit.jupiter.api.Test;
+
+/** Application test */
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
- * Solution: Inserted assertion to check whether the execution of the main method in {@link App}
- * throws an exception.
+ *
Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
*/
-
@Test
void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
+ 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 93ac792ae061..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-2021 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 == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
+ 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 c8c7d66ca4cc..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-2021 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,9 +29,7 @@
import com.iluwatar.ambassador.util.RandomProvider;
import org.junit.jupiter.api.Test;
-/**
- * Test for {@link RemoteService}
- */
+/** Test for {@link RemoteService} */
class RemoteServiceTest {
@Test
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
index a17e36d742c1..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-2021 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 == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
+ 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 66cdb20af9d1..000000000000
--- a/api-gateway/README.md
+++ /dev/null
@@ -1,162 +0,0 @@
----
-layout: pattern
-title: API Gateway
-folder: api-gateway
-permalink: /patterns/api-gateway/
-categories: Architectural
-tags:
- - Cloud distributed
- - Decoupling
- - Microservices
----
-
-## 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.
-
-## Explanation
-
-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.
-
-Real world example
-
-> We are implementing microservices and API Gateway pattern for an e-commerce site. In this system
-> the API Gateway makes calls to the Image and Price microservices.
-
-In plain words
-
-> For a system implemented using microservices architecture, API Gateway is the single entry point
-> that aggregates the calls to the individual microservices.
-
-Wikipedia says
-
-> API Gateway is a server that acts as an API front-end, receives API requests, enforces throttling
-> and security policies, passes requests to the back-end service and then passes the response back
-> to the requester. A gateway often includes a transformation engine to orchestrate and modify the
-> requests and responses on the fly. A gateway can also provide functionality such as collecting
-> analytics data and providing caching. The gateway can provide functionality to support
-> authentication, authorization, security, audit and regulatory compliance.
-
-**Programmatic Example**
-
-This implementation shows what the API Gateway pattern could look like for an e-commerce site. The
-`ApiGateway` makes calls to the Image and Price microservices using the `ImageClientImpl` and
-`PriceClientImpl` respectively. Customers viewing the site on a desktop device can see both price
-information and an image of a product, so the `ApiGateway` calls both of the microservices and
-aggregates the data in the `DesktopProduct` model. However, mobile users only see price information;
-they do not see a product image. For mobile users, the `ApiGateway` only retrieves price
-information, which it uses to populate the `MobileProduct`.
-
-Here's the Image microservice implementation.
-
-```java
-public interface ImageClient {
- String getImagePath();
-}
-
-public class ImageClientImpl implements ImageClient {
- @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;
- }
-}
-```
-
-Here's the Price microservice implementation.
-
-```java
-public interface PriceClient {
- String getPrice();
-}
-
-public class PriceClientImpl implements PriceClient {
-
- @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;
- }
-}
-```
-
-Here we can see how API Gateway maps the requests to the microservices.
-
-```java
-public class ApiGateway {
-
- @Resource
- private ImageClient imageClient;
-
- @Resource
- private PriceClient priceClient;
-
- @RequestMapping(path = "/desktop", method = RequestMethod.GET)
- public DesktopProduct getProductDesktop() {
- var desktopProduct = new DesktopProduct();
- desktopProduct.setImagePath(imageClient.getImagePath());
- desktopProduct.setPrice(priceClient.getPrice());
- return desktopProduct;
- }
-
- @RequestMapping(path = "/mobile", method = RequestMethod.GET)
- public MobileProduct getProductMobile() {
- var mobileProduct = new MobileProduct();
- mobileProduct.setPrice(priceClient.getPrice());
- return mobileProduct;
- }
-}
-```
-
-## Class diagram
-
-
-## Applicability
-
-Use the API Gateway pattern when
-
-* You're using microservices architecture 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/)
-* [Microservices Patterns: With examples in Java](https://www.amazon.com/gp/product/1617294543/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617294543&linkId=ac7b6a57f866ac006a309d9086e8cfbd)
-* [Building Microservices: Designing Fine-Grained Systems](https://www.amazon.com/gp/product/1491950358/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1491950358&linkId=4c95ca9831e05e3f0dadb08841d77bf1)
diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml
deleted file mode 100644
index 26c567d9515e..000000000000
--- a/api-gateway/api-gateway-service/pom.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.24.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 df060b5627c2..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * 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.GetMapping;
-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
- */
- @GetMapping("/desktop")
- 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
- */
- @GetMapping("/mobile")
- 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 fbffbf9f6e32..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-2021 Ilkka Seppälä
- *
- * 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/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml
deleted file mode 100644
index ac270a987aa2..000000000000
--- a/api-gateway/image-microservice/pom.xml
+++ /dev/null
@@ -1,83 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.24.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/pom.xml b/api-gateway/pom.xml
deleted file mode 100644
index 45aa6e75b277..000000000000
--- a/api-gateway/pom.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
- java-design-patterns
- com.iluwatar
- 1.24.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 9ee235a9e13f..000000000000
--- a/api-gateway/price-microservice/pom.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.24.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/ar/README.md b/ar/README.md
deleted file mode 100644
index b49d7927f751..000000000000
--- a/ar/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# أنماط التصميم بلغة برمجة الجافا
-
-
-[](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md)
-[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
-[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
-[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-
-[](#contributors-)
-
-
-
-# مقدمة
-
-أنماط التصميم هي أفضل التدريبات التي يمكن للمبرمج استخدامها لحل المشكلات الشائعة عند تصميم تطبيق أو نظام.
-يمكن لأنماط التصميم تسريع عملية التطوير من خلال توفير نماذج تطوير مجربة ومثبتة ومفيدة.
-تساعد إعادة استخدام أنماط التصميم في منع المشكلات الدقيقة التي تسبب مشكلات كبيرة ، كما تعمل على تحسين قابلية قراءة الكود للمهندسين البرمجيات الذين هم على دراية بالأنماط.
-
-# البدء في التعلم
-
-يعرض هذا الموقع أنماط تصميم جافا. تم تطوير الحلول من قبل المبرمجين والمهندسين ذوي الخبرة من مجتمع المصادر المفتوحة. يمكن تصفح الأنماط من خلال أوصافها عالية المستوى أو بالنظر إلى كود المصدر الخاص بها. تم كتابة الشرح على أمثلة الكود المصدري جيدًا ويمكن اعتبارها برامج تعليمية حول كيفية تنفيذ نمط معين. نحن نستخدم تقنيات Java مفتوحة المصدر الأكثر شيوعًا والتي أثبتت جدارتها في مشاريع الجافا.
-قبل الغوص في المادة ، يجب أن تكون على دراية [بمبادئ تصميم البرامج المختلفة](https://java-design-patterns.com/principles/).
-
-يجب أن تكون جميع التصميمات بسيطة قدر الإمكان. يجب أن تبدأ بـ KISS و YAGNI وافعل أبسط شيء يمكن أن يعمل بهذه المبادىء. يجب إستخدام الأنماط فقط عند الحاجة إليها من أجل تمديد البرنامج بشكل عملي.
-
-بمجرد أن تصبح معتادًا على هذه المفاهيم ، يمكنك البدء في التعمق في [أنماط التصميم المتاحة](https://java-design-patterns.com/patterns/) من خلال أي من الأساليب التالية
-
-ابحث عن نمط محدد بالاسم. لا يمكنك العثور على واحد؟ الرجاء الإبلاغ عن نمط جديد [هنا](https://github.com/iluwatar/java-design-patterns/issues).
-استخدام علامات مثل Performance أو Gang of Four أو الوصول إلى البيانات.
-استخدام فئات الأنماط و Creational, Behavioral وغيرها.
-
-نأمل أن تجد الحلول الموجهة للكائنات المعروضة على هذا الموقع مفيدة في مشاريعك البرمجية وأن تستمتع بتعلمها بقدر ما قمنا بتطويرها.
-
-
-# كيف تساهم؟
-
-إذا كنت على استعداد للمساهمة في المشروع ، فستجد المعلومات ذات الصلة في [ويكي المطور الخاص بنا](https://github.com/iluwatar/java-design-patterns/wiki). سنساعدك ونجيب على أسئلتك في [غرفة الدردشة Gitter](https://gitter.im/iluwatar/java-design-patterns).
-
-
-# الترخيص
-
-هذا المشروع مرخص بموجب شروط ترخيص MIT.
\ No newline at end of file
diff --git a/arrange-act-assert/README.md b/arrange-act-assert/README.md
index 23efdd71e90d..584d8601a23e 100644
--- a/arrange-act-assert/README.md
+++ b/arrange-act-assert/README.md
@@ -1,145 +1,167 @@
---
-layout: pattern
-title: Arrange/Act/Assert
-folder: arrange-act-assert
-permalink: /patterns/arrange-act-assert/
-categories: Idiom
-tags:
- - Testing
+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
+* Given/When/Then
-## Intent
+## Intent of Arrange/Act/Assert Design Pattern
-Arrange/Act/Assert (AAA) is a pattern for organizing unit tests.
-It breaks tests down into three clear and distinct steps:
+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).
-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.
+## Detailed Explanation of Arrange/Act/Assert Pattern with Real-World Examples
-## Explanation
+Real-world example
-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.
+> 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.
-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. It makes tests more natural to write at
-the same time since you already have an outline.
+In plain words
-Real world example
+> Arrange/Act/Assert is a testing pattern that organizes tests into three clear steps for easy maintenance.
-> We need to write comprehensive and clear unit test suite for a class.
+WikiWikiWeb says
-In plain words
+> Arrange/Act/Assert is a pattern for arranging and formatting code in UnitTest methods.
-> Arrange/Act/Assert is a testing pattern that organizes tests into three clear steps for easy
-> maintenance.
+Flowchart
-WikiWikiWeb says
+
-> Arrange/Act/Assert is a pattern for arranging and formatting code in UnitTest methods.
+## Programmatic Example of Arrange/Act/Assert Pattern in Java
-**Programmatic Example**
+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;
+ private int amount;
- Cash(int amount) {
- this.amount = amount;
- }
+ Cash(int amount) {
+ this.amount = amount;
+ }
- void plus(int addend) {
- amount += addend;
- }
+ void plus(int addend) {
+ amount += addend;
+ }
- boolean minus(int subtrahend) {
- if (amount >= subtrahend) {
- amount -= subtrahend;
- return true;
- } else {
- return false;
+ boolean minus(int subtrahend) {
+ if (amount >= subtrahend) {
+ amount -= subtrahend;
+ return true;
+ } else {
+ return false;
+ }
}
- }
- int count() {
- return amount;
- }
+ 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.
+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());
- }
+ @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());
+ }
}
```
-## Applicability
+## When to Use the Arrange/Act/Assert Pattern in Java
Use Arrange/Act/Assert pattern when
-* You need to structure your unit tests so that they're easier to read, maintain, and enhance.
+* 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.
-## Credits
+## 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](https://xp123.com/articles/3a-arrange-act-assert/)
-* [Martin Fowler: GivenWhenThen](https://martinfowler.com/bliki/GivenWhenThen.html)
-* [xUnit Test Patterns: Refactoring Test Code](https://www.amazon.com/gp/product/0131495054/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0131495054&linkId=99701e8f4af2f7e8dd50d720c9b63dbf)
-* [Unit Testing Principles, Practices, and Patterns](https://www.amazon.com/gp/product/1617296279/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617296279&linkId=74c75cf22a63c3e4758ae08aa0a0cc35)
-* [Test Driven Development: By Example](https://www.amazon.com/gp/product/0321146530/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321146530&linkId=5c63a93d8c1175b84ca5087472ef0e05)
+* [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/pom.xml b/arrange-act-assert/pom.xml
index e232371979bb..6b8ba1b2d490 100644
--- a/arrange-act-assert/pom.xml
+++ b/arrange-act-assert/pom.xml
@@ -1,8 +1,10 @@
-
+
java-design-patterns
com.iluwatar
- 1.24.0-SNAPSHOT
+ 1.26.0-SNAPSHOT
4.0.0
-
arrange-act-assert
@@ -41,4 +40,4 @@
test
-
\ No newline at end of file
+
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
index 53b002f0adc4..0c31b1f89f44 100644
--- a/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java
+++ b/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.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-2021 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.arrangeactassert;
import lombok.AllArgsConstructor;
@@ -34,12 +35,12 @@ public class Cash {
private int amount;
- //plus
+ // plus
void plus(int addend) {
amount += addend;
}
- //minus
+ // minus
boolean minus(int subtrahend) {
if (amount >= subtrahend) {
amount -= subtrahend;
@@ -49,7 +50,7 @@ boolean minus(int subtrahend) {
}
}
- //count
+ // 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
index 96c12b478506..ebb261277080 100644
--- a/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java
+++ b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.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-2021 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.arrangeactassert;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -31,11 +32,14 @@
/**
* 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.
+ * 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
@@ -47,53 +51,52 @@
* 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
+ *
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
+ // Arrange
var cash = new Cash(3);
- //Act
+ // Act
cash.plus(4);
- //Assert
+ // Assert
assertEquals(7, cash.count());
}
@Test
void testMinus() {
- //Arrange
+ // Arrange
var cash = new Cash(8);
- //Act
+ // Act
var result = cash.minus(5);
- //Assert
+ // Assert
assertTrue(result);
assertEquals(3, cash.count());
}
@Test
void testInsufficientMinus() {
- //Arrange
+ // Arrange
var cash = new Cash(1);
- //Act
+ // Act
var result = cash.minus(6);
- //Assert
+ // Assert
assertFalse(result);
assertEquals(1, cash.count());
}
@Test
void testUpdate() {
- //Arrange
+ // Arrange
var cash = new Cash(5);
- //Act
+ // Act
cash.plus(6);
var result = cash.minus(3);
- //Assert
+ // 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
index 9fe52ce4c337..5756822516b8 100644
--- a/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java
+++ b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.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-2021 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.arrangeactassert;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -36,23 +37,22 @@
* 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
+ // initialize
var cash = new Cash(3);
- //test plus
+ // test plus
cash.plus(4);
assertEquals(7, cash.count());
- //test minus
+ // test minus
cash = new Cash(8);
assertTrue(cash.minus(5));
assertEquals(3, cash.count());
assertFalse(cash.minus(6));
assertEquals(3, cash.count());
- //test update
+ // test update
cash.plus(5);
assertTrue(cash.minus(5));
assertEquals(3, cash.count());
diff --git a/assets/flags/AR.png b/assets/flags/AR.png
deleted file mode 100644
index f216ab108976..000000000000
Binary files a/assets/flags/AR.png and /dev/null differ
diff --git a/assets/flags/CN.png b/assets/flags/CN.png
deleted file mode 100644
index edd5f1de8640..000000000000
Binary files a/assets/flags/CN.png and /dev/null differ
diff --git a/assets/flags/FR.png b/assets/flags/FR.png
deleted file mode 100644
index 0706dcc05694..000000000000
Binary files a/assets/flags/FR.png and /dev/null differ
diff --git a/assets/flags/KR.png b/assets/flags/KR.png
deleted file mode 100644
index d21bef98a8ec..000000000000
Binary files a/assets/flags/KR.png and /dev/null differ
diff --git a/assets/flags/TR.png b/assets/flags/TR.png
deleted file mode 100644
index 70e2bee6c41a..000000000000
Binary files a/assets/flags/TR.png and /dev/null differ
diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md
index 0f56840af0b1..519152e1268c 100644
--- a/async-method-invocation/README.md
+++ b/async-method-invocation/README.md
@@ -1,172 +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
+## Also known as
-Asynchronous method invocation is a 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.
+* Asynchronous Procedure Call
-## Explanation
+## Intent of Async Method Invocation Design Pattern
-Real world example
+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.
-> Launching space rockets is an exciting business. The mission command gives an order to launch and
-> after some undetermined time, the rocket either launches successfully or fails miserably.
+## Detailed Explanation of Async Method Invocation Pattern with Real-World Examples
+
+Real-world example
+
+> Asynchronous method invocation enables non-blocking operations, allowing multiple processes to run concurrently. This pattern is particularly useful in applications requiring high scalability and performance, such as web servers and microservices.
+>
+> In the context of space rockets, an analogous example of the Async Method Invocation pattern can be seen in the communication between the mission control center and the onboard systems of the rocket. When mission control sends a command to the rocket to adjust its trajectory or perform a system check, they do not wait idly for the rocket to complete the task and report back. Instead, mission control continues to monitor other aspects of the mission and manage different tasks. The rocket executes the command asynchronously and sends a status update or result back to mission control once the operation is complete. This allows mission control to efficiently manage multiple concurrent operations without being blocked by any single task, similar to how asynchronous method invocation works in software systems.
In plain words
-> Asynchronous method invocation starts task processing and returns immediately before the task is
-> ready. The results of the task processing are returned to the caller later.
+> 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.
+> 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**
+## 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.
+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;
+ boolean isCompleted();
+
+ T getValue() throws ExecutionException;
+
+ void await() throws InterruptedException;
}
```
```java
public interface AsyncCallback {
- void onComplete(T value, Optional ex);
+ 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;
+ 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.
+`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();
+ @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();
}
- 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("Space rocket <" + result1 + "> launch complete");
- log("Space rocket <" + result2 + "> launch complete");
- log("Space rocket <" + result3 + "> launch complete");
+ 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.
-```java
-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
+```
+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
```
-# Class diagram
-
-
+## When to Use the Async Method Invocation Pattern in Java
-## Applicability
+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
-* You have multiple independent tasks that can run in parallel
-* You need to improve the performance of a group of sequential tasks
-* You have a limited amount of processing capacity or long-running tasks and the caller should not wait for the tasks to be ready
+* 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 examples
+## 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 910e350a8350..d9ddd918c8e9 100644
--- a/async-method-invocation/pom.xml
+++ b/async-method-invocation/pom.xml
@@ -1,8 +1,10 @@
-
+
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.24.0-SNAPSHOT
-
- async-method-invocation
-
-
- 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
-
-
-
-
-
-
-
-
+
+ 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 b7d5e386b83a..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-2021 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.Callable;
@@ -29,15 +30,15 @@
/**
* 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 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
@@ -59,9 +60,9 @@
@Slf4j
public class App {
- /**
- * Program entry point.
- */
+ private static final String ROCKET_LAUNCH_LOG_PATTERN = "Space rocket <%s> launched successfully";
+
+ /** Program entry point. */
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
var executor = new ThreadAsyncExecutor();
@@ -70,8 +71,8 @@ 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("Deploying lunar rover"));
+ final var asyncResult4 =
+ executor.startProcess(lazyval(20, 400), callback("Deploying lunar rover"));
final var asyncResult5 =
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
@@ -87,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("Space rocket <" + result1 + "> launch complete");
- log("Space rocket <" + result2 + "> launch complete");
- log("Space rocket <" + result3 + "> launch complete");
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result1));
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result2));
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result3));
}
/**
* Creates a callable that lazily evaluates to given value with artificial delay.
*
- * @param value value to evaluate
+ * @param 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("Space rocket <" + value + "> launched successfully");
+ log(String.format(ROCKET_LAUNCH_LOG_PATTERN, value));
return value;
};
}
@@ -114,12 +115,16 @@ 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 {
+ 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 e51b0f19012f..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-2021 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 84cb7bec83d8..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-2021 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 d27011a184cf..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-2021 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 b514a4000b43..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-2021 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 db49fae85d45..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-2021 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.async.method.invocation;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Application test
- */
+import org.junit.jupiter.api.Test;
+
+/** Application test */
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
- * Solution: Inserted assertion to check whether the execution of the main method in {@link App}
- * throws an exception.
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
*/
-
@Test
void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
-
+ 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 b644e0e2393b..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-2021 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 9d3550ad5bdc..cdd09cf5b7d1 100644
--- a/balking/README.md
+++ b/balking/README.md
@@ -1,25 +1,25 @@
---
-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
+## Intent of Balking Design Pattern
-Balking Pattern is used to prevent an object from executing a certain code if it is in an incomplete
-or inappropriate state.
+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.
-## Explanation
+## Detailed Explanation of Balking Pattern with Real-World Examples
-Real world example
+Real-world example
-> 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.
+> 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
@@ -27,54 +27,57 @@ In plain words
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.
+> 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.
-**Programmatic Example**
+Flowchart
-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.
+
+
+## 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;
+ 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());
}
- 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());
- }
}
```
@@ -82,27 +85,29 @@ Here's the simple `DelayProvider` interface used by the `WashingMachine`.
```java
public interface DelayProvider {
- void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
+ void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
}
```
-Now we introduce the application using the `WashingMachine`.
+Now, we introduce the application using the `WashingMachine`.
```java
- public static void main(String... args) {
+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.execute(washingMachine::wash);
}
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();
+ LOGGER.error("ERROR: Waiting on executor service shutdown!");
+ Thread.currentThread().interrupt();
}
- }
+}
```
Here is the console output of the program.
@@ -117,23 +122,40 @@ Here is the console output of the program.
14:02:52.324 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed.
```
-## Class diagram
-
-
-
-## Applicability
+## When to Use the Balking Pattern in Java
Use the Balking pattern when
* You want to invoke an action on an object only when it is in a particular state
-* Objects are generally only in a state that is prone to balking temporarily but for an unknown
- amount of time
+* 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 patterns
+## Related Java Design Patterns
-* [Guarded Suspension Pattern](https://java-design-patterns.com/patterns/guarded-suspension/)
-* [Double Checked Locking Pattern](https://java-design-patterns.com/patterns/double-checked-locking/)
+* [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.
-## Credits
+## References and Credits
-* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML, 2nd Edition, Volume 1](https://www.amazon.com/gp/product/0471227293/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0471227293&linkId=0e39a59ffaab93fb476036fecb637b99)
+* [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 1b200663b090..818f7549c330 100644
--- a/balking/pom.xml
+++ b/balking/pom.xml
@@ -1,8 +1,10 @@
-
-
- java-design-patterns
- com.iluwatar
- 1.24.0-SNAPSHOT
-
- 4.0.0
-
- balking
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.balking.App
-
-
-
-
-
-
-
-
+
+
+ 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 644a344f4d8e..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-2021 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 java.util.concurrent.Executors;
diff --git a/balking/src/main/java/com/iluwatar/balking/DelayProvider.java b/balking/src/main/java/com/iluwatar/balking/DelayProvider.java
index 9b21e9574d1e..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-2021 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 1da2b02043f4..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-2021 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 lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-/**
- * Washing machine class.
- */
+/** Washing machine class. */
@Slf4j
public class WashingMachine {
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) {
- LOGGER.error("", ie);
- Thread.currentThread().interrupt();
- }
- task.run();
- });
+ this(
+ (interval, timeUnit, task) -> {
+ try {
+ Thread.sleep(timeUnit.toMillis(interval));
+ } catch (InterruptedException ie) {
+ LOGGER.error("", ie);
+ Thread.currentThread().interrupt();
+ }
+ task.run();
+ });
}
/**
@@ -59,13 +59,7 @@ 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();
@@ -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 f652a841fbca..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-2021 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;
/**
diff --git a/balking/src/test/java/com/iluwatar/balking/AppTest.java b/balking/src/test/java/com/iluwatar/balking/AppTest.java
index 9c3fa683ad8f..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-2021 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,30 +22,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.balking;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.function.Executable;
-
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.
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
*/
-
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow((Executable) App::main);
}
-
-}
\ 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 8f41bf36e71a..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-2021 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 12a6358f54e1..26567b292c81 100644
--- a/bridge/README.md
+++ b/bridge/README.md
@@ -1,207 +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
+* Handle/Body
-## Intent
+## Intent of Bridge Design Pattern
-Decouple an abstraction from its implementation so that the two can vary independently.
+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.
-## Explanation
+## Detailed Explanation of Bridge Pattern with Real-World Examples
-Real world example
+Real-world example
-> 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.
+> 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
-Translating our weapon example from above. Here we have the `Weapon` hierarchy:
+
+
+## Programmatic Example of Bridge Pattern in Java
+
+Imagine you have a weapon that can have various enchantments, and you need to combine different weapons with different enchantments. How would you handle this? Would you create multiple copies of each weapon, each with a different enchantment, or would you create separate enchantments and apply them to the weapon as needed? The Bridge pattern enables you to do the latter.
+
+Here we have the `Weapon` hierarchy:
```java
public interface Weapon {
- void wield();
- void swing();
- void unwield();
- Enchantment getEnchantment();
+ 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;
+ }
}
```
-Here's 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.");
+ }
}
```
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();
+}
+```
+
+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.
```
-## Class diagram
+## 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:
-## Applicability
+* 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.
-Use the Bridge pattern when
+Trade-offs:
-* 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.
+* 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.
-## Tutorial
+## Related Java Design Patterns
-* [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](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
-* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
+* [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 7fbed70e956e..3cfd33997c05 100644
--- a/bridge/pom.xml
+++ b/bridge/pom.xml
@@ -1,8 +1,10 @@
-
+
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.24.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 639d53f5869c..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-2021 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 lombok.extern.slf4j.Slf4j;
@@ -34,9 +35,9 @@
* 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 {
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java b/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java
index 3ddc34f9fddf..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-2021 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 436b11a701a3..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-2021 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.bridge;
import lombok.extern.slf4j.Slf4j;
-/**
- * FlyingEnchantment.
- */
+/** FlyingEnchantment. */
@Slf4j
public class FlyingEnchantment implements Enchantment {
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Hammer.java b/bridge/src/main/java/com/iluwatar/bridge/Hammer.java
index 834a2c54aa05..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-2021 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.bridge;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-/**
- * Hammer.
- */
+/** Hammer. */
@Slf4j
@AllArgsConstructor
public class Hammer implements Weapon {
@@ -43,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 746b827f6d41..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-2021 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.bridge;
import lombok.extern.slf4j.Slf4j;
-/**
- * SoulEatingEnchantment.
- */
+/** SoulEatingEnchantment. */
@Slf4j
public class SoulEatingEnchantment implements Enchantment {
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Sword.java b/bridge/src/main/java/com/iluwatar/bridge/Sword.java
index ef9764ecafe0..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-2021 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.bridge;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-/**
- * Sword.
- */
+/** Sword. */
@Slf4j
@AllArgsConstructor
public class Sword implements Weapon {
@@ -43,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 c5e1b5074855..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-2021 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 da2019815ac5..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-2021 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,23 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.bridge;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Application test
- */
+import org.junit.jupiter.api.Test;
+
+/** Application test */
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
- * Solution: Inserted assertion to check whether the execution of the main method in {@link App}
- * throws an exception.
+ *
Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
*/
-
@Test
void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
+ 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 678913c2a17a..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-2021 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 7bba8599c310..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-2021 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 e8c4ef2773a8..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-2021 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 008854ed77b2..cf6e3242dd51 100644
--- a/builder/README.md
+++ b/builder/README.md
@@ -1,77 +1,78 @@
---
-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
+## Intent of Builder Design Pattern
-Separate the construction of a complex object from its representation so that the same construction
-process can create different representations.
+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.
-## Explanation
+## Detailed Explanation of Builder Pattern with Real-World Examples
-Real world example
+Real-world example
-> Imagine a character generator for a role-playing game. The easiest option is to let the computer
-> create the character for you. If you want to manually 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.
+> The Java Builder pattern is particularly useful in scenarios where object creation involves numerous parameters.
+>
+> Imagine you are building a customizable sandwich at a deli. The Builder design pattern in this context would involve a SandwichBuilder that allows you to specify each component of the sandwich, such as the type of bread, meat, cheese, vegetables, and condiments. Instead of having to know how to construct the sandwich from scratch, you use the SandwichBuilder to add each desired component step-by-step, ensuring you get exactly the sandwich you want. This separation of construction from the final product representation ensures that the same construction process can yield different types of sandwiches based on the specified components.
In plain words
-> Allows you to create different flavors of an object while avoiding constructor pollution. Useful
-> when there could be several flavors of an object. Or when there are a lot of steps involved in
-> creation of an object.
+> Allows you to create different flavors of an object while avoiding constructor pollution. Useful when there could be several flavors of an object. Or when there are a lot of steps involved in creation of an object.
Wikipedia says
-> The builder pattern is an object creation software design pattern with the intentions of finding
-> a solution to the telescoping constructor 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 may 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 the 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.
-**Programmatic Example**
+Sequence diagram
-The sane alternative is to use the Builder pattern. First of all we have our hero that we want to
-create:
+
+
+## Programmatic Example of Builder Pattern in Java
+
+In this Java Builder pattern example, we construct different types of `Hero` objects with varying attributes.
+
+Imagine a character generator for a role-playing game. The simplest option is to let the computer generate the character for you. However, if you prefer to manually select character details such as profession, gender, hair color, etc., the character creation becomes a step-by-step process that concludes once all selections are made.
+
+A more sensible approach is to use the Builder pattern. First, let's consider the `Hero` that we want to create:
```java
public final class Hero {
- private final Profession profession;
- private final String name;
- private final HairType hairType;
- private final HairColor hairColor;
- private final Armor armor;
- private final Weapon weapon;
-
- private Hero(Builder builder) {
- this.profession = builder.profession;
- this.name = builder.name;
- this.hairColor = builder.hairColor;
- this.hairType = builder.hairType;
- this.weapon = builder.weapon;
- this.armor = builder.armor;
- }
+ private final Profession profession;
+ private final String name;
+ private final HairType hairType;
+ private final HairColor hairColor;
+ private final Armor armor;
+ private final Weapon weapon;
+
+ private Hero(Builder builder) {
+ this.profession = builder.profession;
+ this.name = builder.name;
+ this.hairColor = builder.hairColor;
+ this.hairType = builder.hairType;
+ this.weapon = builder.weapon;
+ this.armor = builder.armor;
+ }
}
```
-Then we have the builder:
+Then we have the `Builder`:
```java
public static class Builder {
@@ -83,68 +84,121 @@ 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);
}
- }
+}
```
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.
+```
-## Applicability
+## When to Use the Builder Pattern in Java
Use the Builder pattern when
+* The Builder pattern is ideal for Java applications requiring complex object creation.
* The algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled
* The construction process must allow different representations for the object that's constructed
+* It's particularly useful when a product requires a lot of steps to be created and when these steps need to be executed in a specific sequence
+
+## Builder Pattern Java Tutorials
+
+* [Builder Design Pattern in Java (DigitalOcean)](https://www.journaldev.com/1425/builder-design-pattern-in-java)
+* [Builder (Refactoring Guru)](https://refactoring.guru/design-patterns/builder)
+* [Exploring Joshua Bloch’s Builder design pattern in Java (Java Magazine)](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java)
-## Real world examples
+## Real-World Applications of Builder 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-)
+* 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](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
-* [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb)
-* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
-* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
+* [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 439ce282a054..3677c187d5e6 100644
--- a/builder/pom.xml
+++ b/builder/pom.xml
@@ -1,8 +1,10 @@
-
+
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.24.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 e62dd8ceb7ff..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-2021 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 com.iluwatar.builder.Hero.Builder;
@@ -28,7 +29,7 @@
/**
* 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.
@@ -57,22 +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 0ed7be12e1ff..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-2021 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,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.builder;
import lombok.AllArgsConstructor;
-/**
- * Armor enumeration.
- */
+/** Armor enumeration. */
@AllArgsConstructor
public enum Armor {
-
CLOTHES("clothes"),
LEATHER("leather"),
CHAIN_MAIL("chain mail"),
diff --git a/builder/src/main/java/com/iluwatar/builder/HairColor.java b/builder/src/main/java/com/iluwatar/builder/HairColor.java
index 361bd5556581..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-2021 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.builder;
-/**
- * HairColor enumeration.
- */
+/** HairColor enumeration. */
public enum HairColor {
-
WHITE,
BLOND,
RED,
@@ -38,5 +36,4 @@ public enum HairColor {
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 9342031f6475..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-2021 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,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.builder;
import lombok.AllArgsConstructor;
-/**
- * HairType enumeration.
- */
+/** HairType enumeration. */
@AllArgsConstructor
public enum HairType {
-
BALD("bald"),
SHORT("short"),
CURLY("curly"),
diff --git a/builder/src/main/java/com/iluwatar/builder/Hero.java b/builder/src/main/java/com/iluwatar/builder/Hero.java
index 7d45bd3df7a2..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-2021 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 f4c25fe89a32..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-2021 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 c80349287e36..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-2021 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 1c9ccddfa584..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-2021 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,23 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.builder;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Application test
- */
+import org.junit.jupiter.api.Test;
+
+/** Application test */
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
- * Solution: Inserted assertion to check whether the execution of the main method in {@link App}
- * throws an exception.
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
*/
-
@Test
void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
+ 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 f456c5ed9b74..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-2021 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 886781632d5a..9f83e1a1748a 100644
--- a/business-delegate/README.md
+++ b/business-delegate/README.md
@@ -1,121 +1,132 @@
---
-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
+## Also known as
-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.
+* Service Representative
-## Explanation
+## Intent of Business Delegate Design Pattern
-Real world example
+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.
-> A mobile phone application promises to stream any movie in existence to your phone. 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.
+## 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.
+> 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.
+> 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**
+## 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();
+ void doProcessing();
}
@Slf4j
public class NetflixService implements VideoStreamingService {
- @Override
- public void doProcessing() {
- LOGGER.info("NetflixService is now processing");
- }
+ @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");
- }
+ @Override
+ public void doProcessing() {
+ LOGGER.info("YouTubeService is now processing");
+ }
}
```
-Then we have a lookup service that decides which video streaming service is used.
+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;
+ private NetflixService netflixService;
+ private YouTubeService youTubeService;
- public VideoStreamingService getBusinessService(String movie) {
- if (movie.toLowerCase(Locale.ROOT).contains("die hard")) {
- return netflixService;
- } else {
- return 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.
+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;
+ private BusinessLookup lookupService;
- public void playbackMovie(String movie) {
- VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie);
- videoStreamingService.doProcessing();
- }
+ public void playbackMovie(String movie) {
+ VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie);
+ videoStreamingService.doProcessing();
+ }
}
```
-The mobile client utilizes business delegate to call the business tier.
+The mobile client utilizes Business Delegate to call the business tier.
```java
public class MobileClient {
- private final BusinessDelegate businessDelegate;
+ private final BusinessDelegate businessDelegate;
- public MobileClient(BusinessDelegate businessDelegate) {
- this.businessDelegate = businessDelegate;
- }
+ public MobileClient(BusinessDelegate businessDelegate) {
+ this.businessDelegate = businessDelegate;
+ }
- public void playbackMovie(String movie) {
- businessDelegate.playbackMovie(movie);
- }
+ public void playbackMovie(String movie) {
+ businessDelegate.playbackMovie(movie);
+ }
}
```
-Finally, we can show the full example in action.
+Finally, we can demonstrate the complete example in action.
```java
- public static void main(String[] args) {
+public static void main(String[] args) {
// prepare the objects
var businessDelegate = new BusinessDelegate();
@@ -128,7 +139,7 @@ Finally, we can show the full example in action.
var client = new MobileClient(businessDelegate);
client.playbackMovie("Die Hard 2");
client.playbackMovie("Maradona: The Greatest Ever");
- }
+}
```
Here is the console output.
@@ -138,27 +149,45 @@ Here is the console output.
21:15:33.794 [main] INFO com.iluwatar.business.delegate.YouTubeService - YouTubeService is now processing
```
-## Class diagram
+## When to Use the Business Delegate Pattern in Java
-
+Use the Business Delegate pattern when
-## Related patterns
+* 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
-* [Service locator pattern](https://java-design-patterns.com/patterns/service-locator/)
+## Business Delegate Pattern Java Tutorials
-## Applicability
+* [Design Patterns - Business Delegate Pattern (TutorialsPoint)](https://www.tutorialspoint.com/design_pattern/business_delegate_pattern.htm)
-Use the Business Delegate pattern when
+## Real-World Applications of Business Delegate Pattern in Java
-* 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
+* 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.
-## Tutorials
+## Related Java Design Patterns
-* [Business Delegate Pattern at TutorialsPoint](https://www.tutorialspoint.com/design_pattern/business_delegate_pattern.htm)
+* [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](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
-* [Core J2EE Patterns: Best Practices and Design Strategies](https://www.amazon.com/gp/product/0130648841/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0130648841&linkId=a0100de2b28c71ede8db1757fb2b5947)
+* [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/pom.xml b/business-delegate/pom.xml
index aa70c99ecaad..a5bccb1529e7 100644
--- a/business-delegate/pom.xml
+++ b/business-delegate/pom.xml
@@ -1,8 +1,10 @@
-
+
-
-