docs: collecting parameter docs + formatting

This commit is contained in:
Ilkka Seppälä committed 2024-03-29 14:13:26 +02:00
1 parent ea7bc2a4eb
commit f80cc468b2
23 files changed
+1191 -794

No files matched your search

+76 -45
View File
@@ -2,23 +2,27 @@
title: Abstract Document title: Abstract Document
category: Structural category: Structural
language: en language: en
tag: tag:
- Abstraction - Abstraction
- Extensibility - Extensibility
- Decoupling - Decoupling
--- ---
## Intent ## Intent
The Abstract Document design pattern is a structural design pattern that aims to provide a consistent way to handle hierarchical and tree-like data structures by defining a common interface for various document types. It separates the core document structure from specific data formats, enabling dynamic updates and simplified maintenance. The Abstract Document design pattern is a structural design pattern that aims to provide a consistent way to handle
hierarchical and tree-like data structures by defining a common interface for various document types. It separates the
core document structure from specific data formats, enabling dynamic updates and simplified maintenance.
## Explanation ## Explanation
The Abstract Document pattern enables handling additional, non-static properties. This pattern uses concept of traits to enable type safety and separate properties of different classes into set of interfaces. The Abstract Document pattern enables handling additional, non-static properties. This pattern uses concept of traits to
enable type safety and separate properties of different classes into set of interfaces.
Real world example Real world example
> Consider a car that consists of multiple parts. However, we don't know if the specific car really has all the parts, or just some of them. Our cars are dynamic and extremely flexible. > Consider a car that consists of multiple parts. However, we don't know if the specific car really has all the parts,
> or just some of them. Our cars are dynamic and extremely flexible.
In plain words In plain words
@@ -26,11 +30,15 @@ In plain words
Wikipedia says Wikipedia says
> An object-oriented structural design pattern for organizing objects in loosely typed key-value stores and exposing the data using typed views. The purpose of the pattern is to achieve a high degree of flexibility between components in a strongly typed language where new properties can be added to the object-tree on the fly, without losing the support of type-safety. The pattern makes use of traits to separate different properties of a class into different interfaces. > An object-oriented structural design pattern for organizing objects in loosely typed key-value stores and exposing the
> data using typed views. The purpose of the pattern is to achieve a high degree of flexibility between components in a
> strongly typed language where new properties can be added to the object-tree on the fly, without losing the support of
> type-safety. The pattern makes use of traits to separate different properties of a class into different interfaces.
**Programmatic Example** **Programmatic Example**
Let's first define the base classes `Document` and `AbstractDocument`. They basically make the object hold a property map and any amount of child objects. Let's first define the base classes `Document` and `AbstractDocument`. They basically make the object hold a property
map and any amount of child objects.
```java ```java
public interface Document { public interface Document {
@@ -75,7 +83,9 @@ public abstract class AbstractDocument implements Document {
... ...
} }
``` ```
Next we define an enum `Property` and a set of interfaces for type, price, model and parts. This allows us to create static looking interface to our `Car` class.
Next we define an enum `Property` and a set of interfaces for type, price, model and parts. This allows us to create
static looking interface to our `Car` class.
```java ```java
public enum Property { public enum Property {
@@ -96,6 +106,7 @@ public interface HasPrice extends Document {
return Optional.ofNullable((Number) get(Property.PRICE.toString())); return Optional.ofNullable((Number) get(Property.PRICE.toString()));
} }
} }
public interface HasModel extends Document { public interface HasModel extends Document {
default Optional<String> getModel() { default Optional<String> getModel() {
@@ -127,40 +138,40 @@ And finally here's how we construct and use the `Car` in a full example.
```java ```java
LOGGER.info("Constructing parts and car"); LOGGER.info("Constructing parts and car");
var wheelProperties = Map.of( var wheelProperties=Map.of(
Property.TYPE.toString(), "wheel", Property.TYPE.toString(),"wheel",
Property.MODEL.toString(), "15C", Property.MODEL.toString(),"15C",
Property.PRICE.toString(), 100L); Property.PRICE.toString(),100L);
var doorProperties = Map.of( var doorProperties=Map.of(
Property.TYPE.toString(), "door", Property.TYPE.toString(),"door",
Property.MODEL.toString(), "Lambo", Property.MODEL.toString(),"Lambo",
Property.PRICE.toString(), 300L); Property.PRICE.toString(),300L);
var carProperties = Map.of( var carProperties=Map.of(
Property.MODEL.toString(), "300SL", Property.MODEL.toString(),"300SL",
Property.PRICE.toString(), 10000L, Property.PRICE.toString(),10000L,
Property.PARTS.toString(), List.of(wheelProperties, doorProperties)); Property.PARTS.toString(),List.of(wheelProperties,doorProperties));
var car = new Car(carProperties); var car=new Car(carProperties);
LOGGER.info("Here is our car:"); LOGGER.info("Here is our car:");
LOGGER.info("-> model: {}", car.getModel().orElseThrow()); LOGGER.info("-> model: {}",car.getModel().orElseThrow());
LOGGER.info("-> price: {}", car.getPrice().orElseThrow()); LOGGER.info("-> price: {}",car.getPrice().orElseThrow());
LOGGER.info("-> parts: "); LOGGER.info("-> parts: ");
car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}", car.getParts().forEach(p->LOGGER.info("\t{}/{}/{}",
p.getType().orElse(null), p.getType().orElse(null),
p.getModel().orElse(null), p.getModel().orElse(null),
p.getPrice().orElse(null)) p.getPrice().orElse(null))
); );
// Constructing parts and car // Constructing parts and car
// Here is our car: // Here is our car:
// model: 300SL // model: 300SL
// price: 10000 // price: 10000
// parts: // parts:
// wheel/15C/100 // wheel/15C/100
// door/Lambo/300 // door/Lambo/300
``` ```
## Class diagram ## Class diagram
@@ -169,21 +180,38 @@ And finally here's how we construct and use the `Car` in a full example.
## Applicability ## Applicability
This pattern is particularly useful in scenarios where you have different types of documents that share some common attributes or behaviors, but also have unique attributes or behaviors specific to their individual types. Here are some scenarios where the Abstract Document design pattern can be applicable: This pattern is particularly useful in scenarios where you have different types of documents that share some common
attributes or behaviors, but also have unique attributes or behaviors specific to their individual types. Here are some
scenarios where the Abstract Document design pattern can be applicable:
* Content Management Systems (CMS): In a CMS, you might have various types of content such as articles, images, videos, etc. Each type of content could have shared attributes like creation date, author, and tags, while also having specific attributes like image dimensions for images or video duration for videos. * Content Management Systems (CMS): In a CMS, you might have various types of content such as articles, images, videos,
etc. Each type of content could have shared attributes like creation date, author, and tags, while also having
specific attributes like image dimensions for images or video duration for videos.
* File Systems: If you're designing a file system where different types of files need to be managed, such as documents, images, audio files, and directories, the Abstract Document pattern can help provide a consistent way to access attributes like file size, creation date, etc., while allowing for specific attributes like image resolution or audio duration. * File Systems: If you're designing a file system where different types of files need to be managed, such as documents,
images, audio files, and directories, the Abstract Document pattern can help provide a consistent way to access
attributes like file size, creation date, etc., while allowing for specific attributes like image resolution or audio
duration.
* E-commerce Systems: An e-commerce platform might have different product types such as physical products, digital downloads, and subscriptions. Each type could share common attributes like name, price, and description, while having unique attributes like shipping weight for physical products or download link for digital products. * E-commerce Systems: An e-commerce platform might have different product types such as physical products, digital
downloads, and subscriptions. Each type could share common attributes like name, price, and description, while having
unique attributes like shipping weight for physical products or download link for digital products.
* Medical Records Systems: In healthcare, patient records might include various types of data such as demographics, medical history, test results, and prescriptions. The Abstract Document pattern can help manage shared attributes like patient ID and date of birth, while accommodating specialized attributes like test results or prescribed medications. * Medical Records Systems: In healthcare, patient records might include various types of data such as demographics,
medical history, test results, and prescriptions. The Abstract Document pattern can help manage shared attributes like
patient ID and date of birth, while accommodating specialized attributes like test results or prescribed medications.
* Configuration Management: When dealing with configuration settings for software applications, there can be different types of configuration elements, each with its own set of attributes. The Abstract Document pattern can be used to manage these configuration elements while ensuring a consistent way to access and manipulate their attributes. * Configuration Management: When dealing with configuration settings for software applications, there can be different
types of configuration elements, each with its own set of attributes. The Abstract Document pattern can be used to
manage these configuration elements while ensuring a consistent way to access and manipulate their attributes.
* Educational Platforms: Educational systems might have various types of learning materials such as text-based content, videos, quizzes, and assignments. Common attributes like title, author, and publication date can be shared, while unique attributes like video duration or assignment due dates can be specific to each type. * Educational Platforms: Educational systems might have various types of learning materials such as text-based content,
videos, quizzes, and assignments. Common attributes like title, author, and publication date can be shared, while
unique attributes like video duration or assignment due dates can be specific to each type.
* Project Management Tools: In project management applications, you could have different types of tasks like to-do items, milestones, and issues. The Abstract Document pattern could be used to handle general attributes like task name and assignee, while allowing for specific attributes like milestone date or issue priority. * Project Management Tools: In project management applications, you could have different types of tasks like to-do
items, milestones, and issues. The Abstract Document pattern could be used to handle general attributes like task name
and assignee, while allowing for specific attributes like milestone date or issue priority.
* Documents have diverse and evolving attribute structures. * Documents have diverse and evolving attribute structures.
@@ -193,7 +221,10 @@ This pattern is particularly useful in scenarios where you have different types
* Maintainability and flexibility are critical for the codebase. * Maintainability and flexibility are critical for the codebase.
The key idea behind the Abstract Document design pattern is to provide a flexible and extensible way to manage different types of documents or entities with shared and distinct attributes. By defining a common interface and implementing it across various document types, you can achieve a more organized and consistent approach to handling complex data structures. The key idea behind the Abstract Document design pattern is to provide a flexible and extensible way to manage different
types of documents or entities with shared and distinct attributes. By defining a common interface and implementing it
across various document types, you can achieve a more organized and consistent approach to handling complex data
structures.
## Consequences ## Consequences
+65 -45
View File
@@ -3,9 +3,9 @@ title: Abstract Factory
category: Creational category: Creational
language: en language: en
tag: tag:
- Abstraction - Abstraction
- Decoupling - Decoupling
- Gang of Four - Gang of Four
--- ---
## Also known as ## Also known as
@@ -14,25 +14,32 @@ Kit
## Intent ## Intent
The Abstract Factory design pattern provides a way to create families of related objects without specifying their concrete classes. This allows for code that is independent of the specific classes of objects it uses, promoting flexibility and maintainability. The Abstract Factory design pattern provides a way to create families of related objects without specifying their
concrete classes. This allows for code that is independent of the specific classes of objects it uses, promoting
flexibility and maintainability.
## Explanation ## Explanation
Real-world example Real-world example
> To create a kingdom 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. > To create a kingdom 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.
In plain words In plain words
> A factory of factories; a factory that groups the individual but related/dependent factories together without specifying their concrete classes. > A factory of factories; a factory that groups the individual but related/dependent factories together without
> specifying their concrete classes.
Wikipedia says 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 > 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** **Programmatic Example**
Translating the kingdom example above. First of all, we have some interfaces and implementation for the objects in the kingdom. Translating the kingdom example above. First of all, we have some interfaces and implementation for the objects in the
kingdom.
```java ```java
public interface Castle { public interface Castle {
@@ -50,20 +57,25 @@ public interface Army {
// Elven implementations -> // Elven implementations ->
public class ElfCastle implements Castle { public class ElfCastle implements Castle {
static final String DESCRIPTION = "This is the elven castle!"; static final String DESCRIPTION = "This is the elven castle!";
@Override @Override
public String getDescription() { public String getDescription() {
return DESCRIPTION; return DESCRIPTION;
} }
} }
public class ElfKing implements King { public class ElfKing implements King {
static final String DESCRIPTION = "This is the elven king!"; static final String DESCRIPTION = "This is the elven king!";
@Override @Override
public String getDescription() { public String getDescription() {
return DESCRIPTION; return DESCRIPTION;
} }
} }
public class ElfArmy implements Army { public class ElfArmy implements Army {
static final String DESCRIPTION = "This is the elven Army!"; static final String DESCRIPTION = "This is the elven Army!";
@Override @Override
public String getDescription() { public String getDescription() {
return DESCRIPTION; return DESCRIPTION;
@@ -79,7 +91,9 @@ Then we have the abstraction and implementations for the kingdom factory.
```java ```java
public interface KingdomFactory { public interface KingdomFactory {
Castle createCastle(); Castle createCastle();
King createKing(); King createKing();
Army createArmy(); Army createArmy();
} }
@@ -112,7 +126,7 @@ public class OrcKingdomFactory implements KingdomFactory {
public King createKing() { public King createKing() {
return new OrcKing(); return new OrcKing();
} }
@Override @Override
public Army createArmy() { public Army createArmy() {
return new OrcArmy(); return new OrcArmy();
@@ -120,64 +134,68 @@ public class OrcKingdomFactory implements KingdomFactory {
} }
``` ```
Now we have the abstract factory that lets us make a family of related objects i.e. elven kingdom factory creates elven castle, king and army, etc. Now we have the abstract factory that lets us make a family of related objects i.e. elven kingdom factory creates elven
castle, king and army, etc.
```java ```java
var factory = new ElfKingdomFactory(); var factory=new ElfKingdomFactory();
var castle = factory.createCastle(); var castle=factory.createCastle();
var king = factory.createKing(); var king=factory.createKing();
var army = factory.createArmy(); var army=factory.createArmy();
castle.getDescription(); castle.getDescription();
king.getDescription(); king.getDescription();
army.getDescription(); army.getDescription();
``` ```
Program output: Program output:
```java ```java
This is the elven castle! This is the elven castle!
This is the elven king! This is the elven king!
This is the elven Army! This is the elven Army!
``` ```
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. 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 ```java
public static class FactoryMaker { public static class FactoryMaker {
public enum KingdomType { public enum KingdomType {
ELF, ORC ELF, ORC
} }
public static KingdomFactory makeFactory(KingdomType type) { public static KingdomFactory makeFactory(KingdomType type) {
return switch (type) { return switch (type) {
case ELF -> new ElfKingdomFactory(); case ELF -> new ElfKingdomFactory();
case ORC -> new OrcKingdomFactory(); case ORC -> new OrcKingdomFactory();
}; };
} }
} }
public static void main(String[] args) { public static void main(String[] args) {
var app = new App(); var app = new App();
LOGGER.info("Elf Kingdom"); LOGGER.info("Elf Kingdom");
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF)); app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
LOGGER.info(app.getArmy().getDescription()); LOGGER.info(app.getArmy().getDescription());
LOGGER.info(app.getCastle().getDescription()); LOGGER.info(app.getCastle().getDescription());
LOGGER.info(app.getKing().getDescription()); LOGGER.info(app.getKing().getDescription());
LOGGER.info("Orc Kingdom"); LOGGER.info("Orc Kingdom");
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC)); app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
--similar use of the orc factory --similar use of the orc factory
} }
``` ```
## Class diagram ## Class diagram
![alt text](./etc/abstract-factory.urm.png "Abstract Factory class diagram") ![alt text](./etc/abstract-factory.urm.png "Abstract Factory class diagram")
## Applicability ## Applicability
Use the Abstract Factory pattern when Use the Abstract Factory pattern when
@@ -185,7 +203,8 @@ Use the Abstract Factory pattern when
* The system should be independent of how its products are created, composed, and represented * The system should be independent of how its products are created, composed, and represented
* The system should be configured with one of the multiple families of products * The system should be configured with one of the multiple families of products
* The family of related product objects is designed to be used together, and you need to enforce this constraint * 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 * 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. * 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 need a run-time value to construct a particular dependency
* You want to decide which product to call from a family at runtime. * You want to decide which product to call from a family at runtime.
@@ -193,9 +212,10 @@ Use the Abstract Factory pattern when
* When you need consistency among products * 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. * You don’t want to change existing code when adding new products or families of products to the program.
Example use cases Example use cases
* Selecting to call to the appropriate implementation of FileSystemAcmeService or DatabaseAcmeService or NetworkAcmeService at runtime. * Selecting to call to the appropriate implementation of FileSystemAcmeService or DatabaseAcmeService or
NetworkAcmeService at runtime.
* Unit test case writing becomes much easier * Unit test case writing becomes much easier
* UI tools for different OS * UI tools for different OS
@@ -219,7 +239,7 @@ Trade-offs
## Tutorials ## Tutorials
* [Abstract Factory Pattern Tutorial](https://www.journaldev.com/1418/abstract-factory-design-pattern-in-java) * [Abstract Factory Pattern Tutorial](https://www.journaldev.com/1418/abstract-factory-design-pattern-in-java)
* [Refactoring Guru - Abstract Factory](https://refactoring.guru/design-patterns/abstract-factory) * [Refactoring Guru - Abstract Factory](https://refactoring.guru/design-patterns/abstract-factory)
## Known uses ## Known uses
+54 -49
View File
@@ -3,84 +3,88 @@ title: Active Object
category: Concurrency category: Concurrency
language: en language: en
tag: tag:
- Performance - Performance
--- ---
## Intent ## Intent
The Active Object design pattern provides a safe and reliable way to implement asynchronous behavior in concurrent systems. 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. The Active Object design pattern provides a safe and reliable way to implement asynchronous behavior in concurrent
systems. 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 ## Explanation
The class that implements the active object pattern will contain a self-synchronization mechanism without using 'synchronized' methods. The class that implements the active object pattern will contain a self-synchronization mechanism without using '
synchronized' methods.
Real-world example 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. > 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.
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**
```java ```java
public abstract class ActiveCreature{ public abstract class ActiveCreature {
private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName()); private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
private BlockingQueue<Runnable> requests; private BlockingQueue<Runnable> requests;
private String name; private String name;
private Thread thread; private Thread thread;
public ActiveCreature(String name) { public ActiveCreature(String name) {
this.name = name; this.name = name;
this.requests = new LinkedBlockingQueue<Runnable>(); this.requests = new LinkedBlockingQueue<Runnable>();
thread = new Thread(new Runnable() { thread = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
while (true) { while (true) {
try { try {
requests.take().run(); requests.take().run();
} catch (InterruptedException e) { } catch (InterruptedException e) {
logger.error(e.getMessage()); logger.error(e.getMessage());
}
} }
} }
} }
}
); );
thread.start(); thread.start();
} }
public void eat() throws InterruptedException { public void eat() throws InterruptedException {
requests.put(new Runnable() { requests.put(new Runnable() {
@Override @Override
public void run() { public void run() {
logger.info("{} is eating!",name()); logger.info("{} is eating!", name());
logger.info("{} has finished eating!",name()); logger.info("{} has finished eating!", name());
} }
} }
); );
} }
public void roam() throws InterruptedException { public void roam() throws InterruptedException {
requests.put(new Runnable() { requests.put(new Runnable() {
@Override @Override
public void run() { public void run() {
logger.info("{} has started to roam the wastelands.",name()); logger.info("{} has started to roam the wastelands.", name());
} }
} }
); );
} }
public String name() { public String name() {
return this.name; return this.name;
} }
} }
``` ```
We can see that any class that will extend the ActiveCreature class will have its own thread of control to invoke and execute methods. 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:
@@ -94,29 +98,30 @@ public class Orc extends ActiveCreature {
} }
``` ```
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 ```java
public static void main(String[] args) { public static void main(String[]args){
var app = new App(); var app=new App();
app.run(); app.run();
} }
@Override @Override
public void run() { public void run(){
ActiveCreature creature; ActiveCreature creature;
try { try{
for (int i = 0;i < creatures;i++) { for(int i=0;i<creatures;i++){
creature = new Orc(Orc.class.getSimpleName().toString() + i); creature=new Orc(Orc.class.getSimpleName().toString()+i);
creature.eat(); creature.eat();
creature.roam(); creature.roam();
} }
Thread.sleep(1000); Thread.sleep(1000);
} catch (InterruptedException e) { }catch(InterruptedException e){
logger.error(e.getMessage()); logger.error(e.getMessage());
} }
Runtime.getRuntime().exit(1); Runtime.getRuntime().exit(1);
} }
``` ```
## Class diagram ## Class diagram
+18 -10
View File
@@ -9,14 +9,15 @@ tag:
## Intent ## Intent
The Acyclic Visitor pattern decouples operations from an object hierarchy, allowing you to add new operations without modifying the object structure directly. The Acyclic Visitor pattern decouples operations from an object hierarchy, allowing you to add new operations without
modifying the object structure directly.
## Explanation ## Explanation
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 > 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). > on filtering criteria (is it Unix or DOS compatible modem).
In plain words In plain words
@@ -24,7 +25,7 @@ In plain words
[WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) says [WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) says
> The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies without affecting those > 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. > hierarchies, and without creating the dependency cycles that are inherent to the GangOfFour VisitorPattern.
**Programmatic Example** **Programmatic Example**
@@ -38,6 +39,7 @@ public abstract class Modem {
public class Zoom extends Modem { public class Zoom extends Modem {
... ...
@Override @Override
public void accept(ModemVisitor modemVisitor) { public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof ZoomVisitor) { if (modemVisitor instanceof ZoomVisitor) {
@@ -50,6 +52,7 @@ public class Zoom extends Modem {
public class Hayes extends Modem { public class Hayes extends Modem {
... ...
@Override @Override
public void accept(ModemVisitor modemVisitor) { public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof HayesVisitor) { if (modemVisitor instanceof HayesVisitor) {
@@ -80,10 +83,12 @@ public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
public class ConfigureForDosVisitor implements AllModemVisitor { public class ConfigureForDosVisitor implements AllModemVisitor {
... ...
@Override @Override
public void visit(Hayes hayes) { public void visit(Hayes hayes) {
LOGGER.info(hayes + " used with Dos configurator."); LOGGER.info(hayes + " used with Dos configurator.");
} }
@Override @Override
public void visit(Zoom zoom) { public void visit(Zoom zoom) {
LOGGER.info(zoom + " used with Dos configurator."); LOGGER.info(zoom + " used with Dos configurator.");
@@ -92,6 +97,7 @@ public class ConfigureForDosVisitor implements AllModemVisitor {
public class ConfigureForUnixVisitor implements ZoomVisitor { public class ConfigureForUnixVisitor implements ZoomVisitor {
... ...
@Override @Override
public void visit(Zoom zoom) { public void visit(Zoom zoom) {
LOGGER.info(zoom + " used with Unix configurator."); LOGGER.info(zoom + " used with Unix configurator.");
@@ -102,10 +108,10 @@ public class ConfigureForUnixVisitor implements ZoomVisitor {
Finally, here are the visitors in action. Finally, here are the visitors in action.
```java ```java
var conUnix = new ConfigureForUnixVisitor(); var conUnix=new ConfigureForUnixVisitor();
var conDos = new ConfigureForDosVisitor(); var conDos=new ConfigureForDosVisitor();
var zoom = new Zoom(); var zoom=new Zoom();
var hayes = new Hayes(); var hayes=new Hayes();
hayes.accept(conDos); hayes.accept(conDos);
zoom.accept(conDos); zoom.accept(conDos);
hayes.accept(conUnix); hayes.accept(conUnix);
@@ -130,7 +136,8 @@ Program output:
This pattern can be used: This pattern can be used:
* When you need to add a new function to an existing hierarchy without the need to alter or affect that hierarchy. * When you need to add a new function to an existing hierarchy without the need to alter or affect that hierarchy.
* When there are functions that operate upon a hierarchy, but which do not belong in the hierarchy itself. e.g. the ConfigureForDOS / ConfigureForUnix / ConfigureForX issue. * When there are functions that operate upon a hierarchy, but which do not belong in the hierarchy itself. e.g. the
ConfigureForDOS / ConfigureForUnix / ConfigureForX issue.
* When you need to perform very different operations on an object depending upon its type. * When you need to perform very different operations on an object depending upon its type.
* When the visited class hierarchy will be frequently extended with new derivatives of the Element class. * When the 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. * When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
@@ -149,7 +156,8 @@ Benefits:
Trade-offs: 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. * 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. * Parallel hierarchy of visitors has to be created for all members in visitable class hierarchy.
## Related patterns ## Related patterns
+32 -19
View File
@@ -14,14 +14,18 @@ Wrapper
## Intent ## Intent
The Adapter pattern converts the interface of a class into another interface that clients expect, enabling compatibility. The Adapter pattern converts the interface of a class into another interface that clients expect, enabling
compatibility.
## Explanation ## Explanation
Real-world example 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. > Consider that you have some pictures on your memory card and you need to transfer them to your computer. To transfer
> 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. > 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 > Yet another example would be a translator translating words spoken by one person to another
In plain words In plain words
@@ -30,7 +34,9 @@ In plain words
Wikipedia says 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. > 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** **Programmatic Example**
@@ -57,6 +63,7 @@ And captain expects an implementation of `RowingBoat` interface to be able to mo
public class Captain { public class Captain {
private final RowingBoat rowingBoat; private final RowingBoat rowingBoat;
// default constructor and setter for rowingBoat // default constructor and setter for rowingBoat
public Captain(RowingBoat rowingBoat) { public Captain(RowingBoat rowingBoat) {
this.rowingBoat = rowingBoat; this.rowingBoat = rowingBoat;
@@ -68,9 +75,11 @@ public class Captain {
} }
``` ```
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. 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 ```java
@Slf4j @Slf4j
public class FishingBoatAdapter implements RowingBoat { public class FishingBoatAdapter implements RowingBoat {
@@ -90,8 +99,8 @@ public class FishingBoatAdapter implements RowingBoat {
And now the `Captain` can use the `FishingBoat` to escape the pirates. And now the `Captain` can use the `FishingBoat` to escape the pirates.
```java ```java
var captain = new Captain(new FishingBoatAdapter()); var captain=new Captain(new FishingBoatAdapter());
captain.row(); captain.row();
``` ```
## Class diagram ## Class diagram
@@ -103,9 +112,13 @@ captain.row();
Use the Adapter pattern when Use the Adapter pattern when
* You want to use an existing class, and its interface does not match the one you need * 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 want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't
* 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. necessarily have compatible interfaces
* 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 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.
## Tutorials ## Tutorials
@@ -114,20 +127,21 @@ Use the Adapter pattern when
* [Baeldung](https://www.baeldung.com/java-adapter-pattern) * [Baeldung](https://www.baeldung.com/java-adapter-pattern)
* [GeeksforGeeks](https://www.geeksforgeeks.org/adapter-pattern/) * [GeeksforGeeks](https://www.geeksforgeeks.org/adapter-pattern/)
## Consequences ## Consequences
Class and object adapters have different trade-offs. A class adapter Class and object adapters have different trade-offs. A class adapter
* 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. * Adapts Adaptee to Target by committing to a concrete Adaptee class. As a consequence, a class adapter won’t work when
* Lets Adapter override some of Adaptee’s behavior since Adapter is a subclass of Adaptee. we want to adapt a class and all its subclasses.
* Introduces only one object, and no additional pointer indirection is needed to get to the adaptee. * Lets 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.
An object adapter An object adapter
* Lets 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 the Adapter refer to the subclass rather than the Adaptee itself.
* Lets 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 the Adapter refer to the
subclass rather than the Adaptee itself.
## Real-world examples ## Real-world examples
@@ -136,7 +150,6 @@ An object adapter
* [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-) * [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-) * [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-)
## 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) * [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)
+37 -17
View File
@@ -3,25 +3,29 @@ title: Aggregator Microservices
category: Architectural category: Architectural
language: en language: en
tag: tag:
- API design - API design
- Cloud distributed - Cloud distributed
- Decoupling - Decoupling
- Microservices - Microservices
--- ---
## Intent ## Intent
Streamline client's interactions with system's microservices by providing a single aggregation point that consolidates data and responses from multiple services. This simplifies the client's communication with the system, improving efficiency and reducing complexity. Streamline client's interactions with system's microservices by providing a single aggregation point that consolidates
data and responses from multiple services. This simplifies the client's communication with the system, improving
efficiency and reducing complexity.
## Explanation ## Explanation
Real world example 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 and product inventory microservices, returning the combined information. > 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 and product inventory microservices, returning the combined
> information.
In plain words In plain words
> Aggregator Microservice collects pieces of data from various microservices and returns an aggregate for processing. > Aggregator Microservice collects pieces of data from various microservices and returns an aggregate for processing.
Stack Overflow says Stack Overflow says
@@ -44,6 +48,7 @@ Next we can introduce our `Aggregator` microservice. It contains clients `Produc
`ProductInventoryClient` for calling respective microservices. `ProductInventoryClient` for calling respective microservices.
```java ```java
@RestController @RestController
public class Aggregator { public class Aggregator {
@@ -75,6 +80,7 @@ Here's the essence of information microservice implementation. Inventory microse
inventory counts. inventory counts.
```java ```java
@RestController @RestController
public class InformationController { public class InformationController {
@RequestMapping(value = "/information", method = RequestMethod.GET) @RequestMapping(value = "/information", method = RequestMethod.GET)
@@ -97,27 +103,41 @@ curl http://localhost:50004/product
## Applicability ## Applicability
The Aggregator Microservices Design Pattern is particularly useful in scenarios where a client requires a composite response that is assembled from data provided by multiple microservices. Common use cases include e-commerce applications where product details, inventory, and reviews might be provided by separate services, or in dashboard applications where aggregated data from various services is displayed in a unified view. The Aggregator Microservices Design Pattern is particularly useful in scenarios where a client requires a composite
response that is assembled from data provided by multiple microservices. Common use cases include e-commerce
applications where product details, inventory, and reviews might be provided by separate services, or in dashboard
applications where aggregated data from various services is displayed in a unified view.
## Consequences ## Consequences
Benefits: Benefits:
* Simplified Client: Clients interact with just one service rather than managing calls to multiple microservices, which simplifies client-side logic. * Simplified Client: Clients interact with just one service rather than managing calls to multiple microservices, which
* Reduced Latency: By aggregating responses, the number of network calls is reduced, which can improve the application's overall latency. simplifies client-side logic.
* Decoupling: Clients are decoupled from the individual microservices, allowing for more flexibility in changing the microservices landscape without impacting clients. * Reduced Latency: By aggregating responses, the number of network calls is reduced, which can improve the application's
* Centralized Logic: Aggregation allows for centralized transformation and logic application on the data collected from various services, which can be more efficient than handling it in the client or spreading it across multiple services. overall latency.
* Decoupling: Clients are decoupled from the individual microservices, allowing for more flexibility in changing the
microservices landscape without impacting clients.
* Centralized Logic: Aggregation allows for centralized transformation and logic application on the data collected from
various services, which can be more efficient than handling it in the client or spreading it across multiple services.
Trade-offs: Trade-offs:
* Single Point of Failure: The aggregator service can become a bottleneck or a single point of failure if not designed with high availability and scalability in mind. * Single Point of Failure: The aggregator service can become a bottleneck or a single point of failure if not designed
* Complexity: Implementing an aggregator can introduce complexity, especially in terms of data aggregation logic and error handling when dealing with multiple services. with high availability and scalability in mind.
* Complexity: Implementing an aggregator can introduce complexity, especially in terms of data aggregation logic and
error handling when dealing with multiple services.
## Related Patterns ## Related Patterns
* [API Gateway](https://java-design-patterns.com/patterns/api-gateway/): The Aggregator Microservices pattern is often used in conjunction with an API Gateway, which provides a single entry point for clients to access multiple microservices. * [API Gateway](https://java-design-patterns.com/patterns/api-gateway/): The Aggregator Microservices pattern is often
* [Composite](https://java-design-patterns.com/patterns/composite/): The Aggregator Microservices pattern can be seen as a form of the Composite pattern, where the composite is the aggregated response from multiple microservices. used in conjunction with an API Gateway, which provides a single entry point for clients to access multiple
* [Facade](https://java-design-patterns.com/patterns/facade/): The Aggregator Microservices pattern can be seen as a form of the Facade pattern, where the facade is the aggregator service that provides a simplified interface to the client. microservices.
* [Composite](https://java-design-patterns.com/patterns/composite/): The Aggregator Microservices pattern can be seen as
a form of the Composite pattern, where the composite is the aggregated response from multiple microservices.
* [Facade](https://java-design-patterns.com/patterns/facade/): The Aggregator Microservices pattern can be seen as a
form of the Facade pattern, where the facade is the aggregator service that provides a simplified interface to the
client.
## Credits ## Credits
+84 -55
View File
@@ -19,68 +19,71 @@ Provide a helper service instance on a client and offload common functionality a
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 > 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 > 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. > frequency should be implemented along with latency checks and client-side logging.
In plain words In plain words
> With the Ambassador pattern, we can implement less-frequent polling from clients along with latency checks and > With the Ambassador pattern, we can implement less-frequent polling from clients along with latency checks and
> logging. > logging.
Microsoft documentation states 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 > 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, > 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, > 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 > 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. > enable a specialized team to implement those features.
**Programmatic Example** **Programmatic Example**
With the above introduction in mind we will imitate the functionality in this example. We have an interface implemented 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: by the remote service as well as the ambassador service:
```java ```java
interface RemoteServiceInterface { interface RemoteServiceInterface {
long doRemoteFunction(int value) throws Exception; long doRemoteFunction(int value) throws Exception;
} }
``` ```
A remote services represented as a singleton. A remote services represented as a singleton.
```java ```java
@Slf4j @Slf4j
public class RemoteService implements RemoteServiceInterface { public class RemoteService implements RemoteServiceInterface {
private static RemoteService service = null; private static RemoteService service = null;
static synchronized RemoteService getRemoteService() { static synchronized RemoteService getRemoteService() {
if (service == null) { if (service == null) {
service = new RemoteService(); service = new RemoteService();
} }
return service; return service;
}
private RemoteService() {
}
@Override
public long doRemoteFunction(int value) {
long waitTime = (long) Math.floor(Math.random() * 1000);
try {
sleep(waitTime);
} catch (InterruptedException e) {
LOGGER.error("Thread sleep interrupted", e);
} }
private RemoteService() {} return waitTime >= 200 ? value * 10 : -1;
}
@Override
public long doRemoteFunction(int value) {
long waitTime = (long) Math.floor(Math.random() * 1000);
try {
sleep(waitTime);
} catch (InterruptedException e) {
LOGGER.error("Thread sleep interrupted", e);
}
return waitTime >= 200 ? value * 10 : -1;
}
} }
``` ```
A service ambassador adding additional features such as logging, latency checks A service ambassador adding additional features such as logging, latency checks
```java ```java
@Slf4j @Slf4j
public class ServiceAmbassador implements RemoteServiceInterface { public class ServiceAmbassador implements RemoteServiceInterface {
private static final int RETRIES = 3; private static final int RETRIES = 3;
@@ -132,6 +135,7 @@ public class ServiceAmbassador implements RemoteServiceInterface {
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 ```java
@Slf4j @Slf4j
public class Client { public class Client {
private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador(); private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
@@ -160,15 +164,15 @@ public class App {
Here's the output for running the example: Here's the output for running the example:
```java ```java
Time taken (ms): 111 Time taken(ms):111
Service result: 120 Service result:120
Time taken (ms): 931 Time taken(ms):931
Failed to reach remote: (1) Failed to reach remote:(1)
Time taken (ms): 665 Time taken(ms):665
Failed to reach remote: (2) Failed to reach remote:(2)
Time taken (ms): 538 Time taken(ms):538
Failed to reach remote: (3) Failed to reach remote:(3)
Service result: -1 Service result:-1
``` ```
## Class diagram ## Class diagram
@@ -177,8 +181,10 @@ Service result: -1
## Applicability ## Applicability
* Cloud Native and Microservices Architectures: Especially useful in distributed systems where it's crucial to monitor, log, and secure inter-service communication. * Cloud Native and Microservices Architectures: Especially useful in distributed systems where it's crucial to monitor,
* Legacy System Integration: Facilitates communication with newer services by handling necessary but non-core functionalities. log, and secure inter-service communication.
* 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. * Performance Enhancement: Can be used to cache results or compress data to improve communication efficiency.
Typical use cases include: Typical use cases include:
@@ -193,34 +199,57 @@ Typical use cases include:
Benefits: Benefits:
* Separation of Concerns: Offloads cross-cutting concerns from the service logic, leading to cleaner, more maintainable code. * Separation of Concerns: Offloads cross-cutting concerns from the service logic, leading to cleaner, more maintainable
* Reusable Infrastructure Logic: The ambassador pattern allows the same logic (e.g., logging, monitoring) to be reused across multiple services. code.
* Improved Security: Centralizes security features like SSL termination or authentication, reducing the risk of misconfiguration. * 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. * Flexibility: Makes it easier to update or replace infrastructure concerns without modifying the service code.
Trade-offs: Trade-offs:
* Increased Complexity: Adds another layer to the architecture, which can complicate the system design and debugging. * 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. * 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. * Deployment Overhead: Requires additional resources and management for deploying and scaling ambassador services.
## Known uses ## Known uses
* 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). * Service Mesh Implementations: In a service mesh architecture, like Istio or Linkerd, the Ambassador pattern is often
* 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. employed as a sidecar proxy that handles inter-service communications. This includes tasks such as service discovery,
* 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. routing, load balancing, telemetry (metrics and tracing), and security (authentication and authorization).
* 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. * API Gateways: API gateways can use the Ambassador pattern to encapsulate common functionalities like rate limiting,
* 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. caching, request shaping, and authentication. This allows backend services to focus on their core business logic
* 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. without being burdened by these cross-cutting concerns.
* 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. * Logging and Monitoring: An Ambassador can aggregate logs and metrics from various services and forward them to
* 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. 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) * [Kubernetes-native API gateway for microservices](https://github.com/datawire/ambassador)
## Related patterns ## Related patterns
* [Proxy](https://java-design-patterns.com/patterns/proxy/): Shares similarities with the proxy pattern, but the ambassador pattern specifically focuses on offloading ancillary functionalities. * [Proxy](https://java-design-patterns.com/patterns/proxy/): Shares similarities with the proxy pattern, but the
* Sidecar: A similar pattern used in the context of containerized applications, where a sidecar container provides additional functionality to the main application container. ambassador pattern specifically focuses on offloading ancillary functionalities.
* [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. * Sidecar: A similar pattern used in the context of containerized applications, where a sidecar container provides
additional functionality to the main application container.
* [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.
## Credits ## Credits
+80 -49
View File
@@ -16,21 +16,34 @@ tag:
## Intent ## Intent
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. 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.
## Explanation ## Explanation
### Context and problem ### 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. 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. 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. 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 ### 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. 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.
### Programmatic example ### Programmatic example
@@ -40,19 +53,25 @@ The example shows why the anti-corruption layer is needed.
Here are 2 shop-ordering systems: `Legacy` and `Modern`. 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. 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. 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 #### Domain model of the `Legacy` system
```java ```java
public class LegacyOrder { public class LegacyOrder {
private String id; private String id;
private String customer; private String customer;
private String item; private String item;
private String qty; private String qty;
private String price; private String price;
} }
``` ```
@@ -60,20 +79,22 @@ public class LegacyOrder {
```java ```java
public class ModernOrder { public class ModernOrder {
private String id; private String id;
private Customer customer; private Customer customer;
private Shipment shipment; private Shipment shipment;
private String extra; private String extra;
} }
public class Customer { public class Customer {
private String address; private String address;
} }
public class Shipment { public class Shipment {
private String item; private String item;
private String qty; private String qty;
private String price; private String price;
} }
``` ```
@@ -82,44 +103,46 @@ public class Shipment {
```java ```java
public class AntiCorruptionLayer { public class AntiCorruptionLayer {
@Autowired @Autowired
private ModernShop modernShop; private ModernShop modernShop;
@Autowired @Autowired
private LegacyShop legacyShop; private LegacyShop legacyShop;
public Optional<LegacyOrder> findOrderInModernSystem(String id) { public Optional<LegacyOrder> findOrderInModernSystem(String id) {
return modernShop.findOrder(id).map(o -> /* map to legacyOrder*/); return modernShop.findOrder(id).map(o -> /* map to legacyOrder*/);
} }
public Optional<ModernOrder> findOrderInLegacySystem(String id) { public Optional<ModernOrder> findOrderInLegacySystem(String id) {
return legacyShop.findOrder(id).map(o -> /* map to modernOrder*/); return legacyShop.findOrder(id).map(o -> /* map to modernOrder*/);
} }
} }
``` ```
#### The connection #### The connection
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. 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 ```java
public class LegacyShop { public class LegacyShop {
@Autowired @Autowired
private AntiCorruptionLayer acl; private AntiCorruptionLayer acl;
public void placeOrder(LegacyOrder legacyOrder) throws ShopException { public void placeOrder(LegacyOrder legacyOrder) throws ShopException {
String id = legacyOrder.getId(); String id = legacyOrder.getId();
Optional<LegacyOrder> orderInModernSystem = acl.findOrderInModernSystem(id); Optional<LegacyOrder> orderInModernSystem = acl.findOrderInModernSystem(id);
if (orderInModernSystem.isPresent()) { if (orderInModernSystem.isPresent()) {
// order is already in the modern system // order is already in the modern system
} else { } else {
// place order in the current system // place order in the current system
}
} }
}
} }
``` ```
@@ -127,11 +150,14 @@ public class LegacyShop {
Use this pattern when: Use this pattern when:
* A migration is planned to happen over multiple stages, but integration between new and legacy systems needs to be maintained * 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 * 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 * 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 * 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 * When there is a need to ensure loose coupling between different subsystems or external services to facilitate easier
maintenance and scalability
## Tutorials ## Tutorials
@@ -140,9 +166,11 @@ Use this pattern when:
## Known Uses ## Known Uses
* Microservices architectures where individual services must communicate without being tightly coupled to each other’s data schemas * 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 * 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 * In bounded contexts within Domain-Driven Design (DDD) to maintain the integrity of a domain model when interacting
with external systems or subsystems
## Consequences ## Consequences
@@ -160,9 +188,12 @@ Trade-offs:
## Related Patterns ## Related Patterns
* [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 * [Facade](https://java-design-patterns.com/patterns/facade/): The Anti-Corruption Layer can be seen as a specialized
* [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 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 * [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
* [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
## Credits ## Credits
+35 -29
View File
@@ -11,7 +11,9 @@ tag:
## Intent ## Intent
The API Gateway design pattern aims to provide a unified interface to a set of microservices. It acts as a single entry point for clients, routing requests to the appropriate microservices and aggregating results, thereby simplifying the client-side code. The API Gateway design pattern aims to provide a unified interface to a set of microservices. It acts as a single entry
point for clients, routing requests to the appropriate microservices and aggregating results, thereby simplifying the
client-side code.
## Also known as ## Also known as
@@ -19,47 +21,47 @@ The API Gateway design pattern aims to provide a unified interface to a set of m
## Explanation ## Explanation
With the Microservices pattern, a client may need data from multiple different microservices. If the 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 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 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 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 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 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. 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 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. 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 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 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. Gateway then calls each of the microservices that the client needs.
Real world example Real world example
> We are implementing microservices and API Gateway pattern for an e-commerce site. In this system > 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. > the API Gateway makes calls to the Image and Price microservices.
In plain words In plain words
> For a system implemented using microservices architecture, API Gateway is the single entry point > For a system implemented using microservices architecture, API Gateway is the single entry point
> that aggregates the calls to the individual microservices. > that aggregates the calls to the individual microservices.
Wikipedia says Wikipedia says
> API Gateway is a server that acts as an API front-end, receives API requests, enforces throttling > 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 > 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 > 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 > 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 > analytics data and providing caching. The gateway can provide functionality to support
> authentication, authorization, security, audit and regulatory compliance. > authentication, authorization, security, audit and regulatory compliance.
**Programmatic Example** **Programmatic Example**
This implementation shows what the API Gateway pattern could look like for an e-commerce site. The 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 `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 `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 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; 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 they do not see a product image. For mobile users, the `ApiGateway` only retrieves price
information, which it uses to populate the `MobileProduct`. information, which it uses to populate the `MobileProduct`.
Here's the Image microservice implementation. Here's the Image microservice implementation.
@@ -153,7 +155,8 @@ public class ApiGateway {
## Applicability ## Applicability
* When building a microservices architecture, and there's a need to abstract the complexity of microservices from the client. * When building a microservices architecture, and there's a need to abstract the complexity of microservices from the
client.
* When multiple microservices need to be consumed in a single request. * When multiple microservices need to be consumed in a single request.
* For authentication, authorization, and security enforcement at a single point. * For authentication, authorization, and security enforcement at a single point.
* To optimize communication between clients and services, especially in a cloud environment. * To optimize communication between clients and services, especially in a cloud environment.
@@ -181,9 +184,12 @@ Trade-offs:
## Related patterns ## Related patterns
* [Aggregator Microservice](../aggregator-microservices/README.md) - The API Gateway pattern is often used in conjunction with the Aggregator Microservice pattern to provide a unified interface to a set of microservices. * [Aggregator Microservice](../aggregator-microservices/README.md) - The API Gateway pattern is often used in
* [Proxy](../proxy/README.md) - The API Gateway pattern is a specialized form of the Proxy pattern, where the gateway acts as a single entry point for clients, routing requests to the appropriate microservices and aggregating results. conjunction with the Aggregator Microservice pattern to provide a unified interface to a set of microservices.
* [Circuit Breaker](../circuit-breaker/README.md) - API Gateways can use the Circuit Breaker pattern to prevent cascading failures when calling multiple microservices. * [Proxy](../proxy/README.md) - The API Gateway pattern is a specialized form of the Proxy pattern, where the gateway
acts as a single entry point for clients, routing requests to the appropriate microservices and aggregating results.
* [Circuit Breaker](../circuit-breaker/README.md) - API Gateways can use the Circuit Breaker pattern to prevent
cascading failures when calling multiple microservices.
## Tutorials ## Tutorials
+8 -5
View File
@@ -42,7 +42,7 @@ In plain words
WikiWikiWeb says WikiWikiWeb says
> Arrange/Act/Assert is a pattern for arranging and formatting code in UnitTest methods. > Arrange/Act/Assert is a pattern for arranging and formatting code in UnitTest methods.
**Programmatic Example** **Programmatic Example**
@@ -132,13 +132,14 @@ class CashAAATest {
Use Arrange/Act/Assert pattern when Use Arrange/Act/Assert pattern when
* Unit testing, especially within the context of TDD and BDD * Unit testing, especially within the context of TDD and BDD
* Anywhere clarity and structure are needed in test cases * Anywhere clarity and structure are needed in test cases
## Known uses ## Known uses
* Widely adopted in software projects using TDD and BDD methodologies. * Widely adopted in software projects using TDD and BDD methodologies.
* Utilized in various programming languages and testing frameworks, such as JUnit (Java), NUnit (.NET), and xUnit frameworks. * Utilized in various programming languages and testing frameworks, such as JUnit (Java), NUnit (.NET), and xUnit
frameworks.
## Consequences ## Consequences
@@ -151,11 +152,13 @@ Benefits:
Trade-offs: Trade-offs:
* May introduce redundancy in tests, as similar arrangements may be repeated across tests. * 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. * Some complex tests might not fit neatly into this structure, requiring additional context or setup outside these three
phases.
## Related patterns ## Related 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. * [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 ## Credits
+85 -71
View File
@@ -3,17 +3,16 @@ title: Async Method Invocation
category: Concurrency category: Concurrency
language: en language: en
tag: tag:
- Asynchronous - Asynchronous
- Reactive - Reactive
- Scalability - Scalability
--- ---
## Intent ## Intent
Asynchronous method invocation is a pattern where the calling thread Asynchronous method invocation is a pattern where the calling thread is not blocked while waiting results of tasks. The
is not blocked while waiting results of tasks. The pattern provides parallel pattern provides parallel processing of multiple independent tasks and retrieving the results via callbacks or waiting
processing of multiple independent tasks and retrieving the results via until everything is done.
callbacks or waiting until everything is done.
## Also known as ## Also known as
@@ -23,54 +22,59 @@ callbacks or waiting until everything is done.
Real world example Real world example
> Launching space rockets is an exciting business. The mission command gives an order to launch and > 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. > after some undetermined time, the rocket either launches successfully or fails miserably.
In plain words In plain words
> Asynchronous method invocation starts task processing and returns immediately before the task is > 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. > ready. The results of the task processing are returned to the caller later.
Wikipedia says Wikipedia says
> In multithreaded computer programming, asynchronous method invocation (AMI), also known as > 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 > 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 > 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. > notified when the reply arrives. Polling for a reply is an undesired option.
**Programmatic Example** **Programmatic Example**
In this example, we are launching space rockets and deploying lunar rovers. 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 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, `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 `AsyncCallback` which can be provided to be executed on task completion and `AsyncExecutor` that
manages the execution of the async tasks. manages the execution of the async tasks.
```java ```java
public interface AsyncResult<T> { public interface AsyncResult<T> {
boolean isCompleted(); boolean isCompleted();
T getValue() throws ExecutionException; T getValue() throws ExecutionException;
void await() throws InterruptedException; void await() throws InterruptedException;
} }
``` ```
```java ```java
public interface AsyncCallback<T> { public interface AsyncCallback<T> {
void onComplete(T value); void onComplete(T value);
void onError(Exception ex);
void onError(Exception ex);
} }
``` ```
```java ```java
public interface AsyncExecutor { public interface AsyncExecutor {
<T> AsyncResult<T> startProcess(Callable<T> task); <T> AsyncResult<T> startProcess(Callable<T> task);
<T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback); <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback);
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException; <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
} }
``` ```
`ThreadAsyncExecutor` is an implementation of `AsyncExecutor`. Some of its key parts are highlighted `ThreadAsyncExecutor` is an implementation of `AsyncExecutor`. Some of its key parts are highlighted
next. next.
```java ```java
@@ -85,14 +89,14 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) { public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
var result = new CompletableResult<>(callback); var result = new CompletableResult<>(callback);
new Thread( new Thread(
() -> { () -> {
try { try {
result.setValue(task.call()); result.setValue(task.call());
} catch (Exception ex) { } catch (Exception ex) {
result.setException(ex); result.setException(ex);
} }
}, },
"executor-" + idx.incrementAndGet()) "executor-" + idx.incrementAndGet())
.start(); .start();
return result; return result;
} }
@@ -111,50 +115,50 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
Then we are ready to launch some rockets to see how everything works together. Then we are ready to launch some rockets to see how everything works together.
```java ```java
public static void main(String[] args) throws Exception { public static void main(String[]args)throws Exception{
// construct a new executor that will run async tasks // construct a new executor that will run async tasks
var executor = new ThreadAsyncExecutor(); var executor=new ThreadAsyncExecutor();
// start few async tasks with varying processing times, two last with callback handlers // start few async tasks with varying processing times, two last with callback handlers
final var asyncResult1 = executor.startProcess(lazyval(10, 500)); final var asyncResult1=executor.startProcess(lazyval(10,500));
final var asyncResult2 = executor.startProcess(lazyval("test", 300)); final var asyncResult2=executor.startProcess(lazyval("test",300));
final var asyncResult3 = executor.startProcess(lazyval(50L, 700)); 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 = final var asyncResult5=
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover")); executor.startProcess(lazyval("callback",600),callback("Deploying lunar rover"));
// emulate processing in the current thread while async tasks are running in their own threads // 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 Thread.sleep(350); // Oh boy, we are working hard here
log("Mission command is sipping coffee"); log("Mission command is sipping coffee");
// wait for completion of the tasks // wait for completion of the tasks
final var result1 = executor.endProcess(asyncResult1); final var result1=executor.endProcess(asyncResult1);
final var result2 = executor.endProcess(asyncResult2); final var result2=executor.endProcess(asyncResult2);
final var result3 = executor.endProcess(asyncResult3); final var result3=executor.endProcess(asyncResult3);
asyncResult4.await(); asyncResult4.await();
asyncResult5.await(); asyncResult5.await();
// log the results of the tasks, callbacks log immediately when complete // log the results of the tasks, callbacks log immediately when complete
log("Space rocket <" + result1 + "> launch complete"); log("Space rocket <"+result1+"> launch complete");
log("Space rocket <" + result2 + "> launch complete"); log("Space rocket <"+result2+"> launch complete");
log("Space rocket <" + result3 + "> launch complete"); log("Space rocket <"+result3+"> launch complete");
} }
``` ```
Here's the program console output. Here's the program console output.
```java ```java
21:47:08.227 [executor-2] INFO com.iluwatar.async.method.invocation.App - Space rocket <test> launched successfully 21:47:08.227[executor-2]INFO com.iluwatar.async.method.invocation.App-Space rocket<test> launched successfully
21:47:08.269 [main] INFO com.iluwatar.async.method.invocation.App - Mission command is sipping coffee 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.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.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.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 <callback> launched successfully 21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Space rocket<callback> launched successfully
21:47:08.519 [executor-5] INFO com.iluwatar.async.method.invocation.App - Deploying lunar rover <callback> 21:47:08.519[executor-5]INFO com.iluwatar.async.method.invocation.App-Deploying lunar rover<callback>
21:47:08.616 [executor-3] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launched successfully 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<10>launch complete
21:47:08.617 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <test> launch complete 21:47:08.617[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<test> launch complete
21:47:08.618 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launch complete 21:47:08.618[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launch complete
``` ```
# Class diagram # Class diagram
@@ -166,14 +170,16 @@ Here's the program console output.
Use the async method invocation pattern when Use the async method invocation pattern when
* When operations do not need to complete before proceeding with the next steps in a program. * 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. * 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 GUI applications to prevent freezing or unresponsiveness during long-running tasks.
* In web applications for non-blocking IO operations. * In web applications for non-blocking IO operations.
## Known Uses ## Known Uses
* Web servers handling HTTP requests asynchronously to improve throughput and reduce latency. * 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. * 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. * 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) * [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) * [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html)
@@ -184,21 +190,29 @@ Use the async method invocation pattern when
Benefits: Benefits:
* Improved Responsiveness: The main thread or application flow remains unblocked, improving the user experience in GUI applications and overall responsiveness. * Improved Responsiveness: The main thread or application flow remains unblocked, improving the user experience in GUI
* Better Resource Utilization: By enabling parallel execution, system resources (like CPU and IO) are utilized more efficiently, potentially improving the application's throughput. 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. * Scalability: Easier to scale applications, as tasks can be distributed across available resources more effectively.
Trade-offs: Trade-offs:
* Complexity: Introducing asynchronous operations can complicate the codebase, making it more challenging to understand, debug, and maintain. * Complexity: Introducing asynchronous operations can complicate the codebase, making it more challenging to understand,
* Resource Management: Requires careful management of threads or execution contexts, which can introduce overhead and potential resource exhaustion issues. debug, and maintain.
* Error Handling: Asynchronous operations can make error handling more complex, as exceptions may occur in different threads or at different times. * 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 Patterns: Related Patterns:
* [Command](https://java-design-patterns.com/patterns/command/): Asynchronous method invocation can be used to implement the Command pattern, where commands are executed asynchronously. * [Command](https://java-design-patterns.com/patterns/command/): Asynchronous method invocation can be used to implement
* [Observer](https://java-design-patterns.com/patterns/observer/): Asynchronous method invocation can be used to notify observers asynchronously when a subject's state changes. the Command pattern, where commands are executed asynchronously.
* [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. * [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.
## Credits ## Credits
+34 -26
View File
@@ -3,18 +3,19 @@ title: Balking
category: Concurrency category: Concurrency
language: en language: en
tag: tag:
- Decoupling - Decoupling
--- ---
## Intent ## Intent
Balking Pattern is used to prevent an object from executing a certain code if it is in an incomplete or inappropriate state. If the state is not suitable for the action, the method call is ignored (or "balked"). Balking Pattern is used to prevent an object from executing a certain code if it is in an incomplete or inappropriate
state. If the state is not suitable for the action, the method call is ignored (or "balked").
## Explanation ## Explanation
Real world example Real world example
> There's a start-button in a washing machine to initiate the laundry washing. When the washing > 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 > machine is inactive the button works as expected, but if it's already washing the button does
> nothing. > nothing.
@@ -24,21 +25,22 @@ In plain words
Wikipedia says Wikipedia says
> The balking pattern is a software design pattern that only executes an action on an object when > 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 > 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" > method invokes a get method on the object when the ZIP file is not open, the object would "balk"
> at the request. > at the request.
**Programmatic Example** **Programmatic Example**
In this example implementation, `WashingMachine` is an object that has two states in which it can 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 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()` 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. it won't do that and returns without doing anything.
Here are the relevant parts of the `WashingMachine` class. Here are the relevant parts of the `WashingMachine` class.
```java ```java
@Slf4j @Slf4j
public class WashingMachine { public class WashingMachine {
@@ -86,20 +88,20 @@ public interface DelayProvider {
Now we introduce the application using the `WashingMachine`. Now we introduce the application using the `WashingMachine`.
```java ```java
public static void main(String... args) { public static void main(String...args){
final var washingMachine = new WashingMachine(); final var washingMachine=new WashingMachine();
var executorService = Executors.newFixedThreadPool(3); var executorService=Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) { for(int i=0;i< 3;i++){
executorService.execute(washingMachine::wash); executorService.execute(washingMachine::wash);
} }
executorService.shutdown(); executorService.shutdown();
try { try{
executorService.awaitTermination(10, TimeUnit.SECONDS); executorService.awaitTermination(10,TimeUnit.SECONDS);
} catch (InterruptedException ie) { }catch(InterruptedException ie){
LOGGER.error("ERROR: Waiting on executor service shutdown!"); LOGGER.error("ERROR: Waiting on executor service shutdown!");
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
}
} }
}
``` ```
Here is the console output of the program. Here is the console output of the program.
@@ -123,27 +125,33 @@ Here is the console output of the program.
Use the Balking pattern when Use the Balking pattern when
* You want to invoke an action on an object only when it is in a particular state * 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 * Objects are generally only in a state that is prone to balking temporarily but for an unknown
amount of time 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. * 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.
## Known Uses: ## Known Uses:
* Resource pooling, where resources are only allocated if they are in a valid state for allocation. * 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. * Thread management, where threads only proceed with tasks if certain conditions (like task availability or resource
locks) are met.
## Consequences: ## Consequences:
Benefits: Benefits:
* Reduces unnecessary lock acquisitions in situations where actions cannot proceed, enhancing performance in concurrent applications. * 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. * 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. * Simplifies the handling of operations that should only be performed under certain conditions without cluttering the
caller code with state checks.
Trade-offs: 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. * Can introduce complexity by obscuring the conditions under which actions are taken or ignored, potentially making the
* May lead to missed opportunities or actions if the state changes are not properly monitored or if the balking condition is too restrictive. 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 patterns
+55 -27
View File
@@ -20,15 +20,20 @@ Decouple an abstraction from its implementation so that the two can vary indepen
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. > 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 Plain Words In Plain Words
> Bridge pattern is about preferring composition to inheritance. Implementation details are pushed from a hierarchy to another object with a separate hierarchy. > Bridge pattern is about preferring composition to inheritance. Implementation details are pushed from a hierarchy to
> another object with a separate hierarchy.
Wikipedia says 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" > 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** **Programmatic Example**
@@ -37,8 +42,11 @@ Translating our weapon example from above. Here we have the `Weapon` hierarchy:
```java ```java
public interface Weapon { public interface Weapon {
void wield(); void wield();
void swing(); void swing();
void unwield(); void unwield();
Enchantment getEnchantment(); Enchantment getEnchantment();
} }
@@ -112,7 +120,9 @@ Here's the separate enchantment hierarchy:
```java ```java
public interface Enchantment { public interface Enchantment {
void onActivate(); void onActivate();
void apply(); void apply();
void onDeactivate(); void onDeactivate();
} }
@@ -157,16 +167,16 @@ Here are both hierarchies in action:
```java ```java
LOGGER.info("The knight receives an enchanted sword."); LOGGER.info("The knight receives an enchanted sword.");
var enchantedSword = new Sword(new SoulEatingEnchantment()); var enchantedSword=new Sword(new SoulEatingEnchantment());
enchantedSword.wield(); enchantedSword.wield();
enchantedSword.swing(); enchantedSword.swing();
enchantedSword.unwield(); enchantedSword.unwield();
LOGGER.info("The valkyrie receives an enchanted hammer."); LOGGER.info("The valkyrie receives an enchanted hammer.");
var hammer = new Hammer(new FlyingEnchantment()); var hammer=new Hammer(new FlyingEnchantment());
hammer.wield(); hammer.wield();
hammer.swing(); hammer.swing();
hammer.unwield(); hammer.unwield();
``` ```
Here's the console output. Here's the console output.
@@ -196,37 +206,55 @@ The item's glow fades.
Use the Bridge pattern when Use the Bridge pattern when
* 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. * You want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for
* 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. example, when the implementation must be selected or switched at run-time.
* Changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled. * Both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern
* 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. lets you combine the different abstractions and implementations and extend them independently.
* 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. * 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.
## Known uses ## Known uses
* GUI Frameworks where the abstraction is the window, and the implementation could be the underlying OS windowing system. * GUI Frameworks where the abstraction is the window, and the implementation could be the underlying OS windowing
* Database Drivers where the abstraction is a generic database interface, and the implementations are database-specific drivers. system.
* Device Drivers where the abstraction is the device-independent code, and the implementation is the device-dependent code. * 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.
## Consequences ## Consequences
Benefits: Benefits:
* Decoupling Interface and Implementation: The Bridge pattern enhances modularity by separating the interface (the high-level operations) from the implementation (the low-level operations). * 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. * Improved Extensibility: You can extend the abstraction and implementation hierarchies independently.
* Hiding Implementation Details: Clients only see the abstraction's interface, not its implementation. * Hiding Implementation Details: Clients only see the abstraction's interface, not its implementation.
Trade-offs: Trade-offs:
* Increased Complexity: The pattern can complicate the system architecture and code, especially for clients unfamiliar with the pattern. * Increased Complexity: The pattern can complicate the system architecture and code, especially for clients unfamiliar
* Runtime Overhead: The extra layer of abstraction can introduce a performance penalty, although it is often negligible in practice. with the pattern.
* Runtime Overhead: The extra layer of abstraction can introduce a performance penalty, although it is often negligible
in practice.
## Related Patterns ## Related Patterns
* [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. * [Adapter](https://java-design-patterns.com/patterns/adapter/): The Adapter pattern is used to provide a different
* [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. interface to an object, while the Bridge pattern is used to separate an object's interface from its implementation.
* [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. * [Strategy](https://java-design-patterns.com/patterns/strategy/): The Strategy pattern is like the Bridge pattern, but
* [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. 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.
* [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.
* [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.
## Tutorials ## Tutorials
+66 -51
View File
@@ -3,35 +3,43 @@ title: Builder
category: Creational category: Creational
language: en language: en
tag: tag:
- Gang of Four - Gang of Four
--- ---
## Intent ## Intent
Separate the construction of a complex object from its representation so that the same construction process can create different representations. Separate the construction of a complex object from its representation so that the same construction process can create
different representations.
## Explanation ## Explanation
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. > 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.
In plain words 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 Wikipedia says
> The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor antipattern. > 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 antipattern is. At one point or the other, we have all seen a constructor like below: Having said that let me add a bit about what telescoping constructor antipattern is. At one point or the other, we have
all seen a constructor like below:
```java ```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){
} }
``` ```
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 antipattern. 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 antipattern.
**Programmatic Example** **Programmatic Example**
@@ -61,51 +69,51 @@ Then we have the builder:
```java ```java
public static class Builder { public static class Builder {
private final Profession profession; private final Profession profession;
private final String name; private final String name;
private HairType hairType; private HairType hairType;
private HairColor hairColor; private HairColor hairColor;
private Armor armor; private Armor armor;
private Weapon weapon; private Weapon weapon;
public Builder(Profession profession, String name) { public Builder(Profession profession, String name) {
if (profession == null || name == null) { if (profession == null || name == null) {
throw new IllegalArgumentException("profession and name can not be null"); throw new IllegalArgumentException("profession and name can not be null");
}
this.profession = profession;
this.name = name;
}
public Builder withHairType(HairType hairType) {
this.hairType = hairType;
return this;
}
public Builder withHairColor(HairColor hairColor) {
this.hairColor = hairColor;
return this;
}
public Builder withArmor(Armor armor) {
this.armor = armor;
return this;
}
public Builder withWeapon(Weapon weapon) {
this.weapon = weapon;
return this;
}
public Hero build() {
return new Hero(this);
} }
this.profession = profession;
this.name = name;
} }
public Builder withHairType(HairType hairType) {
this.hairType = hairType;
return this;
}
public Builder withHairColor(HairColor hairColor) {
this.hairColor = hairColor;
return this;
}
public Builder withArmor(Armor armor) {
this.armor = armor;
return this;
}
public Builder withWeapon(Weapon weapon) {
this.weapon = weapon;
return this;
}
public Hero build() {
return new Hero(this);
}
}
``` ```
Then it can be used as: Then it can be used as:
```java ```java
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();
``` ```
## Class diagram ## Class diagram
@@ -116,9 +124,11 @@ var mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.
Use the Builder pattern when Use the Builder pattern when
* The algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled * The algorithm for creating a complex object should be independent of the parts that make up the object and how they're
assembled
* The construction process must allow different representations for the object that's constructed * The 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 * 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
## Known Uses ## Known Uses
@@ -132,7 +142,8 @@ Benefits:
* More control over the construction process compared to other creational patterns * More control over the construction process compared to other creational patterns
* Supports constructing objects step-by-step, defer construction steps or run steps recursively * 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 * 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 * Single Responsibility Principle. You can isolate complex construction code from the business logic of the product
Trade-offs: Trade-offs:
@@ -148,7 +159,8 @@ Trade-offs:
## Known uses ## Known uses
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) * [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.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-) * [java.lang.StringBuffer](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html#append-boolean-)
* All implementations of [java.lang.Appendable](http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html) * 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 Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
@@ -156,7 +168,10 @@ Trade-offs:
## Related patterns ## Related patterns
* [Step Builder](https://java-design-patterns.com/patterns/step-builder/) 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. * [Step Builder](https://java-design-patterns.com/patterns/step-builder/) 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.
## Credits ## Credits
+26 -13
View File
@@ -3,12 +3,14 @@ title: Business Delegate
category: Structural category: Structural
language: en language: en
tag: tag:
- Decoupling - Decoupling
--- ---
## Intent ## Intent
The Business Delegate pattern adds an abstraction layer between presentation and business tiers. By using the pattern we gain loose coupling between the tiers and encapsulate knowledge about how to locate, connect to, and interact with the business objects that make up the application. The Business Delegate pattern adds an abstraction layer between presentation and business tiers. By using the pattern we
gain loose coupling between the tiers and encapsulate knowledge about how to locate, connect to, and interact with the
business objects that make up the application.
## Also known as ## Also known as
@@ -18,7 +20,9 @@ Service Representative
Real world example Real world example
> 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. > 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.
In Plain Words In Plain Words
@@ -26,7 +30,10 @@ In Plain Words
Wikipedia says 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.
**Programmatic Example** **Programmatic Example**
@@ -57,6 +64,7 @@ public class YouTubeService implements VideoStreamingService {
Then, we have a lookup service that decides which video streaming service to use. Then, we have a lookup service that decides which video streaming service to use.
```java ```java
@Setter @Setter
public class BusinessLookup { public class BusinessLookup {
@@ -77,6 +85,7 @@ The Business Delegate uses a business lookup to route movie playback requests to
video streaming service. video streaming service.
```java ```java
@Setter @Setter
public class BusinessDelegate { public class BusinessDelegate {
@@ -109,20 +118,20 @@ public class MobileClient {
Finally, we can demonstrate the complete example in action. Finally, we can demonstrate the complete example in action.
```java ```java
public static void main(String[] args) { public static void main(String[]args){
// prepare the objects // prepare the objects
var businessDelegate = new BusinessDelegate(); var businessDelegate=new BusinessDelegate();
var businessLookup = new BusinessLookup(); var businessLookup=new BusinessLookup();
businessLookup.setNetflixService(new NetflixService()); businessLookup.setNetflixService(new NetflixService());
businessLookup.setYouTubeService(new YouTubeService()); businessLookup.setYouTubeService(new YouTubeService());
businessDelegate.setLookupService(businessLookup); businessDelegate.setLookupService(businessLookup);
// create the client and use the Business Delegate // create the client and use the Business Delegate
var client = new MobileClient(businessDelegate); var client=new MobileClient(businessDelegate);
client.playbackMovie("Die Hard 2"); client.playbackMovie("Die Hard 2");
client.playbackMovie("Maradona: The Greatest Ever"); client.playbackMovie("Maradona: The Greatest Ever");
} }
``` ```
Here is the console output. Here is the console output.
@@ -164,7 +173,8 @@ Benefits:
* Decoupling of Presentation and Business Tiers: Allows the client tier and business services to evolve independently. * 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. * 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. * Reuse and Scalability: Business Delegate objects can be reused by multiple clients, and the pattern supports load
balancing and scalability.
Trade-offs: Trade-offs:
@@ -173,9 +183,12 @@ Trade-offs:
## Related patterns ## Related patterns
* [Service Locator](https://java-design-patterns.com/patterns/service-locator/): Business Delegate uses Service Locator to locate business services. * [Service Locator](https://java-design-patterns.com/patterns/service-locator/): Business Delegate uses Service Locator
* [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. to locate 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. * [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 ## Credits
+34 -17
View File
@@ -3,7 +3,7 @@ title: Bytecode
category: Behavioral category: Behavioral
language: en language: en
tag: tag:
- Game programming - Game programming
--- ---
## Intent ## Intent
@@ -14,22 +14,28 @@ Allows encoding behavior as instructions for a virtual machine.
Real world example Real world example
> A team is working on a new game where wizards battle against each other. The wizard behavior needs to be carefully adjusted and iterated hundreds of times through playtesting. It's not optimal to ask the programmer to make changes each time the game designer wants to vary the behavior, so the wizard behavior is implemented as a data-driven virtual machine. > A team is working on a new game where wizards battle against each other. The wizard behavior needs to be carefully
> adjusted and iterated hundreds of times through playtesting. It's not optimal to ask the programmer to make changes each
> time the game designer wants to vary the behavior, so the wizard behavior is implemented as a data-driven virtual
> machine.
In plain words In plain words
> Bytecode pattern enables behavior driven by data instead of code. > Bytecode pattern enables behavior driven by data instead of code.
[Gameprogrammingpatterns.com](https://gameprogrammingpatterns.com/bytecode.html) documentation [Gameprogrammingpatterns.com](https://gameprogrammingpatterns.com/bytecode.html) documentation
states: states:
> An instruction set defines the low-level operations that can be performed. A series of instructions is encoded as a sequence of bytes. A virtual machine executes these instructions one at a time, using a stack for intermediate values. By combining instructions, complex high-level behavior can be defined. > An instruction set defines the low-level operations that can be performed. A series of instructions is encoded as a
> sequence of bytes. A virtual machine executes these instructions one at a time, using a stack for intermediate values.
> By combining instructions, complex high-level behavior can be defined.
**Programmatic Example** **Programmatic Example**
One of the most important game objects is the `Wizard` class. One of the most important game objects is the `Wizard` class.
```java ```java
@AllArgsConstructor @AllArgsConstructor
@Setter @Setter
@Getter @Getter
@@ -54,9 +60,12 @@ public class Wizard {
} }
``` ```
Next, we show the available instructions for our virtual machine. Each of the instructions has its own semantics on how it operates with the stack data. For example, the ADD instruction takes the top two items from the stack, adds them together and pushes the result to the stack. Next, we show the available instructions for our virtual machine. Each of the instructions has its own semantics on how
it operates with the stack data. For example, the ADD instruction takes the top two items from the stack, adds them
together and pushes the result to the stack.
```java ```java
@AllArgsConstructor @AllArgsConstructor
@Getter @Getter
public enum Instruction { public enum Instruction {
@@ -76,9 +85,11 @@ public enum Instruction {
} }
``` ```
At the heart of our example is the `VirtualMachine` class. It takes instructions as input and executes them to provide the game object behavior. At the heart of our example is the `VirtualMachine` class. It takes instructions as input and executes them to provide
the game object behavior.
```java ```java
@Getter @Getter
@Slf4j @Slf4j
public class VirtualMachine { public class VirtualMachine {
@@ -172,11 +183,11 @@ public class VirtualMachine {
Now we can show the full example utilizing the virtual machine. Now we can show the full example utilizing the virtual machine.
```java ```java
public static void main(String[] args) { public static void main(String[]args){
var vm = new VirtualMachine( var vm=new VirtualMachine(
new Wizard(45, 7, 11, 0, 0), new Wizard(45,7,11,0,0),
new Wizard(36, 18, 8, 0, 0)); new Wizard(36,18,8,0,0));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
@@ -190,7 +201,7 @@ Now we can show the full example utilizing the virtual machine.
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE")); vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH")); vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
} }
``` ```
Here is the console output. Here is the console output.
@@ -216,11 +227,13 @@ Here is the console output.
## Applicability ## Applicability
Use the Bytecode pattern when you have a lot of behavior you need to define and your game’s implementation language isn’t a good fit because: Use the Bytecode pattern when you have a lot of behavior you need to define and your game’s implementation language
isn’t a good fit because:
* It’s too low-level, making it tedious or error-prone to program in. * It’s too low-level, making it tedious or error-prone to program in.
* Iterating on it takes too long due to slow compile times or other tooling issues. * Iterating on it takes too long due to slow compile times or other tooling issues.
* It has too much trust. If you want to ensure the behavior being defined can’t break the game, you need to sandbox it from the rest of the codebase. * It has too much trust. If you want to ensure the behavior being defined can’t break the game, you need to sandbox it
from the rest of the codebase.
## Known Uses ## Known Uses
@@ -238,14 +251,18 @@ Benefits:
Trade-offs: Trade-offs:
* Overhead: Running bytecode typically involves more overhead than running native code, potentially affecting performance. * Overhead: Running bytecode typically involves more overhead than running native code, potentially affecting
performance.
* Complexity: Implementing and maintaining a VM adds complexity to the system. * Complexity: Implementing and maintaining a VM adds complexity to the system.
## Related patterns ## Related patterns
* [Interpreter](https://java-design-patterns.com/patterns/interpreter/) is often used within the implementation of VMs to interpret bytecode instructions * [Interpreter](https://java-design-patterns.com/patterns/interpreter/) is often used within the implementation of VMs
* [Command](https://java-design-patterns.com/patterns/command/): Bytecode instructions can be seen as commands executed by the VM. to interpret bytecode instructions
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): VMs may use factory methods to instantiate operations or instructions defined in the bytecode. * [Command](https://java-design-patterns.com/patterns/command/): Bytecode instructions can be seen as commands executed
by the VM.
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): VMs may use factory methods to
instantiate operations or instructions defined in the bytecode.
## Credits ## Credits
+67 -25
View File
@@ -10,7 +10,9 @@ tag:
## Intent ## Intent
The caching pattern avoids expensive re-acquisition of resources by not releasing them immediately after use. The resources retain their identity, are kept in some fast-access storage, and are re-used to avoid having to acquire them again. The caching pattern avoids expensive re-acquisition of resources by not releasing them immediately after use. The
resources retain their identity, are kept in some fast-access storage, and are re-used to avoid having to acquire them
again.
## Also known as ## Also known as
@@ -21,7 +23,10 @@ The caching pattern avoids expensive re-acquisition of resources by not releasin
Real world example Real world example
> A team is working on a website that provides new homes for abandoned cats. People can post their cats on the website after registering, but all the new posts require approval from one of the site moderators. The user accounts of the site moderators contain a specific flag and the data is stored in a MongoDB database. Checking for the moderator flag each time a post is viewed becomes expensive, and it's a good idea to utilize caching here. > A team is working on a website that provides new homes for abandoned cats. People can post their cats on the website
> after registering, but all the new posts require approval from one of the site moderators. The user accounts of the site
> moderators contain a specific flag and the data is stored in a MongoDB database. Checking for the moderator flag each
> time a post is viewed becomes expensive, and it's a good idea to utilize caching here.
In plain words In plain words
@@ -29,13 +34,20 @@ In plain words
Wikipedia says: Wikipedia says:
> In computing, a cache is a hardware or software component that stores data so that future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it cannot. Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading from a slower data store; thus, the more requests that can be served from the cache, the faster the system performs. > In computing, a cache is a hardware or software component that stores data so that future requests for that data can
> be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored
> elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it cannot.
> Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading from a slower
> data store; thus, the more requests that can be served from the cache, the faster the system performs.
**Programmatic Example** **Programmatic Example**
Let's first look at the data layer of our application. The interesting classes are `UserAccount` which is a simple Java object containing the user account details, and `DbManager` interface which handles reading and writing of these objects to/from database. Let's first look at the data layer of our application. The interesting classes are `UserAccount` which is a simple Java
object containing the user account details, and `DbManager` interface which handles reading and writing of these objects
to/from database.
```java ```java
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@ToString @ToString
@@ -49,11 +61,15 @@ public class UserAccount {
public interface DbManager { public interface DbManager {
void connect(); void connect();
void disconnect(); void disconnect();
UserAccount readFromDb(String userId); UserAccount readFromDb(String userId);
UserAccount writeToDb(UserAccount userAccount); UserAccount writeToDb(UserAccount userAccount);
UserAccount updateDb(UserAccount userAccount); UserAccount updateDb(UserAccount userAccount);
UserAccount upsertDb(UserAccount userAccount); UserAccount upsertDb(UserAccount userAccount);
} }
``` ```
@@ -62,17 +78,21 @@ In the example, we are demonstrating various different caching policies
* Write-through writes data to the cache and DB in a single transaction * Write-through writes data to the cache and DB in a single transaction
* Write-around writes data immediately into the DB instead of the cache * Write-around writes data immediately into the DB instead of the cache
* Write-behind writes data into the cache initially whilst the data is only written into the DB * Write-behind writes data into the cache initially whilst the data is only written into the DB
when the cache is full when the cache is full
* Cache-aside pushes the responsibility of keeping the data synchronized in both data sources to * Cache-aside pushes the responsibility of keeping the data synchronized in both data sources to
the application itself the application itself
* Read-through strategy is also included in the aforementioned strategies, and it returns data from * Read-through strategy is also included in the aforementioned strategies, and it returns data from
the cache to the caller if it exists, otherwise queries from DB and stores it into the cache for the cache to the caller if it exists, otherwise queries from DB and stores it into the cache for
future use. future use.
The cache implementation in `LruCache` is a hash table accompanied by a doubly linked-list. The linked-list helps in capturing and maintaining the LRU data in the cache. When data is queried (from the cache), added (to the cache), or updated, the data is moved to the front of the list to depict itself as the most-recently-used data. The LRU data is always at the end of the list. The cache implementation in `LruCache` is a hash table accompanied by a doubly linked-list. The linked-list helps in
capturing and maintaining the LRU data in the cache. When data is queried (from the cache), added (to the cache), or
updated, the data is moved to the front of the list to depict itself as the most-recently-used data. The LRU data is
always at the end of the list.
```java ```java
@Slf4j @Slf4j
public class LruCache { public class LruCache {
@@ -87,7 +107,7 @@ public class LruCache {
this.userAccount = userAccount; this.userAccount = userAccount;
} }
} }
/* ... omitted details ... */ /* ... omitted details ... */
public LruCache(int capacity) { public LruCache(int capacity) {
@@ -127,14 +147,21 @@ public class LruCache {
public boolean contains(String userId) { public boolean contains(String userId) {
return cache.containsKey(userId); return cache.containsKey(userId);
} }
public void remove(Node node) { /* ... */ } public void remove(Node node) { /* ... */ }
public void setHead(Node node) { /* ... */ } public void setHead(Node node) { /* ... */ }
public void invalidate(String userId) { /* ... */ } public void invalidate(String userId) { /* ... */ }
public boolean isFull() { /* ... */ } public boolean isFull() { /* ... */ }
public UserAccount getLruData() { /* ... */ } public UserAccount getLruData() { /* ... */ }
public void clear() { /* ... */ } public void clear() { /* ... */ }
public List<UserAccount> getCacheDataInListForm() { /* ... */ } public List<UserAccount> getCacheDataInListForm() { /* ... */ }
public void setCapacity(int newCapacity) { /* ... */ } public void setCapacity(int newCapacity) { /* ... */ }
} }
``` ```
@@ -142,6 +169,7 @@ public class LruCache {
The next layer we are going to look at is `CacheStore` which implements the different caching strategies. The next layer we are going to look at is `CacheStore` which implements the different caching strategies.
```java ```java
@Slf4j @Slf4j
public class CacheStore { public class CacheStore {
@@ -201,9 +229,13 @@ public class CacheStore {
} }
``` ```
`AppManager` helps to bridge the gap in communication between the main class and the application's back-end. DB connection is initialized through this class. The chosen caching strategy/policy is also initialized here. Before the cache can be used, the size of the cache has to be set. Depending on the chosen caching policy, `AppManager` will call the appropriate function in the `CacheStore` class. `AppManager` helps to bridge the gap in communication between the main class and the application's back-end. DB
connection is initialized through this class. The chosen caching strategy/policy is also initialized here. Before the
cache can be used, the size of the cache has to be set. Depending on the chosen caching policy, `AppManager` will call
the appropriate function in the `CacheStore` class.
```java ```java
@Slf4j @Slf4j
public final class AppManager { public final class AppManager {
@@ -223,7 +255,7 @@ public final class AppManager {
public UserAccount find(final String userId) { public UserAccount find(final String userId) {
LOGGER.info("Trying to find {} in cache", userId); LOGGER.info("Trying to find {} in cache", userId);
if (cachingPolicy == CachingPolicy.THROUGH if (cachingPolicy == CachingPolicy.THROUGH
|| cachingPolicy == CachingPolicy.AROUND) { || cachingPolicy == CachingPolicy.AROUND) {
return cacheStore.readThrough(userId); return cacheStore.readThrough(userId);
} else if (cachingPolicy == CachingPolicy.BEHIND) { } else if (cachingPolicy == CachingPolicy.BEHIND) {
return cacheStore.readThroughWithWriteBackPolicy(userId); return cacheStore.readThroughWithWriteBackPolicy(userId);
@@ -257,12 +289,13 @@ public final class AppManager {
Here is what we do in the main class of the application. Here is what we do in the main class of the application.
```java ```java
@Slf4j @Slf4j
public class App { public class App {
public static void main(final String[] args) { public static void main(final String[] args) {
boolean isDbMongo = isDbMongo(args); boolean isDbMongo = isDbMongo(args);
if(isDbMongo){ if (isDbMongo) {
LOGGER.info("Using the Mongo database engine to run the application."); LOGGER.info("Using the Mongo database engine to run the application.");
} else { } else {
LOGGER.info("Using the 'in Memory' database to run the application."); LOGGER.info("Using the 'in Memory' database to run the application.");
@@ -308,7 +341,8 @@ public class App {
Use the Caching pattern when Use the Caching pattern when
* Repetitious acquisition, initialization, and release of the same resource cause unnecessary performance overhead * Repetitious acquisition, initialization, and release of the same resource cause unnecessary performance overhead
* In scenarios where the cost of recomputing or re-fetching data is significantly higher than storing and retrieving it from cache * In scenarios where the cost of recomputing or re-fetching data is significantly higher than storing and retrieving it
from cache
* For read-heavy applications with relatively static data or data that changes infrequently * For read-heavy applications with relatively static data or data that changes infrequently
## Known Uses ## Known Uses
@@ -316,28 +350,36 @@ Use the Caching pattern when
* Web page caching to reduce server load and improve response time * Web page caching to reduce server load and improve response time
* Database query caching to avoid repeated expensive SQL queries * Database query caching to avoid repeated expensive SQL queries
* Caching results of CPU-intensive computations * Caching results of CPU-intensive computations
* Content Delivery Networks (CDNs) for caching static resources like images, CSS, and JavaScript files closer to the end users * Content Delivery Networks (CDNs) for caching static resources like images, CSS, and JavaScript files closer to the end
users
## Consequences ## Consequences
Benefits: Benefits:
* Improved Performance: Significantly reduces data access latency, leading to faster application performance * Improved Performance: Significantly reduces data access latency, leading to faster application performance
* Reduced Load: Decreases the load on the underlying data source, which can lead to cost savings and increased longevity of the resource * Reduced Load: Decreases the load on the underlying data source, which can lead to cost savings and increased longevity
* Scalability: Enhances the scalability of applications by efficiently handling increases in load without proportional increases in resource utilization of the resource
* Scalability: Enhances the scalability of applications by efficiently handling increases in load without proportional
increases in resource utilization
Trade-Offs: Trade-Offs:
* Complexity: Introduces complexity in terms of cache invalidation, consistency, and synchronization * Complexity: Introduces complexity in terms of cache invalidation, consistency, and synchronization
* Resource Utilization: Requires additional memory or storage resources to maintain the cache * Resource Utilization: Requires additional memory or storage resources to maintain the cache
* Stale Data: There's a risk of serving outdated data if the cache is not properly invalidated or updated when the underlying data changes * Stale Data: There's a risk of serving outdated data if the cache is not properly invalidated or updated when the
underlying data changes
## Related patterns ## Related patterns
* [Proxy](https://java-design-patterns.com/patterns/proxy/): Caching can be implemented using the Proxy pattern, where the proxy object intercepts requests and returns cached data if available * [Proxy](https://java-design-patterns.com/patterns/proxy/): Caching can be implemented using the Proxy pattern, where
* [Observer](https://java-design-patterns.com/patterns/observer/): Can be used to notify the cache when the underlying data changes, so that it can be updated or invalidated accordingly the proxy object intercepts requests and returns cached data if available
* [Decorator](https://java-design-patterns.com/patterns/decorator/): Can be used to add caching behavior to an existing object without modifying its code * [Observer](https://java-design-patterns.com/patterns/observer/): Can be used to notify the cache when the underlying
* [Strategy](https://java-design-patterns.com/patterns/strategy/): Different caching strategies can be implemented using the Strategy pattern, allowing the application to switch between them at runtime data changes, so that it can be updated or invalidated accordingly
* [Decorator](https://java-design-patterns.com/patterns/decorator/): Can be used to add caching behavior to an existing
object without modifying its code
* [Strategy](https://java-design-patterns.com/patterns/strategy/): Different caching strategies can be implemented using
the Strategy pattern, allowing the application to switch between them at runtime
## Credits ## Credits
+23 -13
View File
@@ -11,7 +11,8 @@ tag:
## Intent ## Intent
Callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time. Callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (
execute) the argument at some convenient time.
## Also known as ## Also known as
@@ -22,15 +23,17 @@ Callback is a piece of executable code that is passed as an argument to other co
Real world example Real world example
> We need to be notified after the executing task has finished. We pass a callback method for the executor and wait for it to call back on us. > We need to be notified after the executing task has finished. We pass a callback method for the executor and wait for
> it to call back on us.
In plain words In plain words
> Callback is a method passed to an executor which will be called at a defined moment. > Callback is a method passed to an executor which will be called at a defined moment.
Wikipedia says Wikipedia says
> In computer programming, a callback, also known as a "call-after" function, is any executable code that is passed as an argument to other code; that other code is expected to call back (execute) the argument at a given time. > In computer programming, a callback, also known as a "call-after" function, is any executable code that is passed as
> an argument to other code; that other code is expected to call back (execute) the argument at a given time.
**Programmatic Example** **Programmatic Example**
@@ -69,8 +72,8 @@ public final class SimpleTask extends Task {
Finally, here's how we execute a task and receive a callback when it's finished. Finally, here's how we execute a task and receive a callback when it's finished.
```java ```java
var task = new SimpleTask(); var task=new SimpleTask();
task.executeWith(() -> LOGGER.info("I'm done now.")); task.executeWith(()->LOGGER.info("I'm done now."));
``` ```
## Class diagram ## Class diagram
@@ -89,13 +92,15 @@ Use the Callback pattern when
* GUI frameworks often use callbacks for event handling, such as user interactions (clicks, key presses) * GUI frameworks often use callbacks for event handling, such as user interactions (clicks, key presses)
* Node.js heavily relies on callbacks for non-blocking I/O operations * Node.js heavily relies on callbacks for non-blocking I/O operations
* Frameworks that deal with asynchronous operations, like Promises in JavaScript, use callbacks to handle the resolution or rejection of asynchronous tasks * Frameworks that deal with asynchronous operations, like Promises in JavaScript, use callbacks to handle the resolution
or rejection of asynchronous tasks
## Consequences ## Consequences
Benefits: Benefits:
* Decouples the execution logic of an operation from the signaling or notification logic, enhancing modularity and reusability * Decouples the execution logic of an operation from the signaling or notification logic, enhancing modularity and
reusability
* Facilitates asynchronous processing, improving the responsiveness and scalability of applications * Facilitates asynchronous processing, improving the responsiveness and scalability of applications
* Enables a reactive programming model where components can react to events as they occur * Enables a reactive programming model where components can react to events as they occur
@@ -103,16 +108,21 @@ Trade-offs:
* Callback hell or pyramid of doom: Deeply nested callbacks can lead to code that is hard to read and maintain * Callback hell or pyramid of doom: Deeply nested callbacks can lead to code that is hard to read and maintain
* Inversion of control can lead to harder-to-follow code flow, making debugging more challenging * Inversion of control can lead to harder-to-follow code flow, making debugging more challenging
* Potential issues with error handling, especially in languages or environments where exceptions are used, as errors might need to be propagated through callbacks * Potential issues with error handling, especially in languages or environments where exceptions are used, as errors
might need to be propagated through callbacks
## Related patterns ## Related patterns
[Observer](https://java-design-patterns.com/patterns/observer/): Callbacks can be seen as a more dynamic and lightweight form of the Observer pattern, with the ability to subscribe and unsubscribe callback functions dynamically [Observer](https://java-design-patterns.com/patterns/observer/): Callbacks can be seen as a more dynamic and lightweight
[Command](https://java-design-patterns.com/patterns/command/): Callbacks can be implemented as Command objects in scenarios where more flexibility or statefulness is required in the callback operation form of the Observer pattern, with the ability to subscribe and unsubscribe callback functions dynamically
[Promise](https://java-design-patterns.com/patterns/promise/): In some languages or frameworks, Promises or Futures can be used to handle asynchronous operations more cleanly, often using callbacks for success or failure cases [Command](https://java-design-patterns.com/patterns/command/): Callbacks can be implemented as Command objects in
scenarios where more flexibility or statefulness is required in the callback operation
[Promise](https://java-design-patterns.com/patterns/promise/): In some languages or frameworks, Promises or Futures can
be used to handle asynchronous operations more cleanly, often using callbacks for success or failure cases
## Real world examples ## Real world examples
* [CyclicBarrier](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept a callback that will be triggered every time a barrier is tripped. * [CyclicBarrier](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29)
constructor can accept a callback that will be triggered every time a barrier is tripped.
* [JavaScript: The Good Parts](https://amzn.to/3TiQV61) * [JavaScript: The Good Parts](https://amzn.to/3TiQV61)
* [Node.js Design Patterns - Third edition: Design and implement production-grade Node.js applications using proven patterns and techniques](https://amzn.to/3VssjKG) * [Node.js Design Patterns - Third edition: Design and implement production-grade Node.js applications using proven patterns and techniques](https://amzn.to/3VssjKG)
+46 -40
View File
@@ -15,27 +15,27 @@ tag:
## Intent ## Intent
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to Avoid coupling the sender of a request to its receiver by giving more than one object a chance to
handle the request. Chain the receiving objects and pass the request along the chain until an object handle the request. Chain the receiving objects and pass the request along the chain until an object
handles it. handles it.
## Explanation ## Explanation
Real-world example Real-world example
> The Orc King gives loud orders to his army. The closest one to react is the commander, then > The Orc King gives loud orders to his army. The closest one to react is the commander, then
> an officer, and then a soldier. The commander, officer, and soldier form a chain of responsibility. > an officer, and then a soldier. The commander, officer, and soldier form a chain of responsibility.
In plain words In plain words
> It helps to build a chain of objects. A request enters from one end and keeps going from an object > It helps to build a chain of objects. A request enters from one end and keeps going from an object
> to another until it finds a suitable handler. > to another until it finds a suitable handler.
Wikipedia says Wikipedia says
> In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of > In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of
> a source of command objects and a series of processing objects. Each processing object contains > a source of command objects and a series of processing objects. Each processing object contains
> logic that defines the types of command objects that it can handle; the rest are passed to the > logic that defines the types of command objects that it can handle; the rest are passed to the
> next processing object in the chain. > next processing object in the chain.
**Programmatic Example** **Programmatic Example**
@@ -56,11 +56,11 @@ public class Request {
this.requestType = Objects.requireNonNull(requestType); this.requestType = Objects.requireNonNull(requestType);
this.requestDescription = Objects.requireNonNull(requestDescription); this.requestDescription = Objects.requireNonNull(requestDescription);
} }
public void markHandled() { public void markHandled() {
this.handled = true; this.handled = true;
} }
@Override @Override
public String toString() { public String toString() {
return getRequestDescription(); return getRequestDescription();
@@ -77,37 +77,37 @@ Next, we show the request handler hierarchy.
```java ```java
public interface RequestHandler { public interface RequestHandler {
boolean canHandleRequest(Request req); boolean canHandleRequest(Request req);
int getPriority(); int getPriority();
void handle(Request req); void handle(Request req);
String name(); String name();
} }
@Slf4j @Slf4j
public class OrcCommander implements RequestHandler { public class OrcCommander implements RequestHandler {
@Override @Override
public boolean canHandleRequest(Request req) { public boolean canHandleRequest(Request req) {
return req.getRequestType() == RequestType.DEFEND_CASTLE; return req.getRequestType() == RequestType.DEFEND_CASTLE;
} }
@Override @Override
public int getPriority() { public int getPriority() {
return 2; return 2;
} }
@Override @Override
public void handle(Request req) { public void handle(Request req) {
req.markHandled(); req.markHandled();
LOGGER.info("{} handling request \"{}\"", name(), req); LOGGER.info("{} handling request \"{}\"", name(), req);
} }
@Override @Override
public String name() { public String name() {
return "Orc commander"; return "Orc commander";
} }
} }
// OrcOfficer and OrcSoldier are defined similarly as OrcCommander // OrcOfficer and OrcSoldier are defined similarly as OrcCommander
@@ -143,10 +143,10 @@ public class OrcKing {
The chain of responsibility in action. The chain of responsibility in action.
```java ```java
var king = new OrcKing(); var king=new OrcKing();
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); king.makeRequest(new Request(RequestType.DEFEND_CASTLE,"defend castle"));
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); king.makeRequest(new Request(RequestType.TORTURE_PRISONER,"torture prisoner"));
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); king.makeRequest(new Request(RequestType.COLLECT_TAX,"collect tax"));
``` ```
The console output. The console output.
@@ -165,7 +165,8 @@ Orc soldier handling request "collect tax"
Use Chain of Responsibility when Use Chain of Responsibility when
* More than one object may handle a request, and the handler isn't known a priori. The handler should be ascertained automatically. * More than one object may handle a request, and the handler isn't known a priori. The handler should be ascertained
automatically.
* You want to issue a request to one of several objects without specifying the receiver explicitly. * You want to issue a request to one of several objects without specifying the receiver explicitly.
* The set of objects that can handle a request should be specified dynamically. * The set of objects that can handle a request should be specified dynamically.
@@ -183,20 +184,25 @@ Use Chain of Responsibility when
Benefits: Benefits:
* Reduced coupling. The sender of a request does not need to know the concrete handler that will process the request. * Reduced coupling. The sender of a request does not need to know the concrete handler that will process the request.
* Increased flexibility in assigning responsibilities to objects. You can add or change responsibilities for handling a request by changing the members and order of the chain. * Increased flexibility in assigning responsibilities to objects. You can add or change responsibilities for handling a
request by changing the members and order of the chain.
* Allows you to set a default handler if no concrete handler can handle the request. * Allows you to set a default handler if no concrete handler can handle the request.
Trade-Offs: Trade-Offs:
* It can be challenging to debug and understand the flow, especially if the chain is long and complex. * It can be challenging to debug and understand the flow, especially if the chain is long and complex.
* The request might end up unhandled if the chain doesn't include a catch-all handler. * The request might end up unhandled if the chain doesn't include a catch-all handler.
* Performance concerns might arise due to potentially going through several handlers before finding the right one, or not finding it at all. * Performance concerns might arise due to potentially going through several handlers before finding the right one, or
not finding it at all.
## Related Patterns ## Related Patterns
[Command](https://java-design-patterns.com/patterns/command/): can be used to encapsulate a request as an object, which might be passed along the chain. [Command](https://java-design-patterns.com/patterns/command/): can be used to encapsulate a request as an object, which
[Composite](https://java-design-patterns.com/patterns/composite/): the Chain of Responsibility is often applied in conjunction with the Composite pattern. might be passed along the chain.
[Decorator](https://java-design-patterns.com/patterns/decorator/): decorators can be chained in a similar manner as responsibilities in the Chain of Responsibility pattern. [Composite](https://java-design-patterns.com/patterns/composite/): the Chain of Responsibility is often applied in
conjunction with the Composite pattern.
[Decorator](https://java-design-patterns.com/patterns/decorator/): decorators can be chained in a similar manner as
responsibilities in the Chain of Responsibility pattern.
## Credits ## Credits
+154 -139
View File
@@ -14,38 +14,41 @@ tag:
## Intent ## Intent
The Circuit Breaker pattern aims to prevent a software system from making calls to a part of the system that is either failing or showing signs of distress. It is a way to gracefully degrade functionality when a dependent service is not responding, rather than failing completely. The Circuit Breaker pattern aims to prevent a software system from making calls to a part of the system that is either
failing or showing signs of distress. It is a way to gracefully degrade functionality when a dependent service is not
responding, rather than failing completely.
## Explanation ## Explanation
Real world example Real world example
> Imagine a web application that has both local files/images and remote services that are used for > Imagine a web application that has both local files/images and remote services that are used for
> fetching data. These remote services may be either healthy and responsive at times, or may become > fetching data. These remote services may be either healthy and responsive at times, or may become
> slow and unresponsive at some point of time due to variety of reasons. So if one of the remote > slow and unresponsive at some point of time due to variety of reasons. So if one of the remote
> services is slow or not responding successfully, our application will try to fetch response from > services is slow or not responding successfully, our application will try to fetch response from
> the remote service using multiple threads/processes, soon all of them will hang (also called > the remote service using multiple threads/processes, soon all of them will hang (also called
> [thread starvation](https://en.wikipedia.org/wiki/Starvation_(computer_science))) causing our entire web application to crash. We should be able to detect > [thread starvation](https://en.wikipedia.org/wiki/Starvation_(computer_science))) causing our entire web application
> this situation and show the user an appropriate message so that he/she can explore other parts of > to crash. We should be able to detect
> the app unaffected by the remote service failure. Meanwhile, the other services that are working > this situation and show the user an appropriate message so that he/she can explore other parts of
> the app unaffected by the remote service failure. Meanwhile, the other services that are working
> normally, should keep functioning unaffected by this failure. > normally, should keep functioning unaffected by this failure.
In plain words In plain words
> Circuit Breaker allows graceful handling of failed remote services. It's especially useful when > Circuit Breaker allows graceful handling of failed remote services. It's especially useful when
> all parts of our application are highly decoupled from each other, and failure of one component > all parts of our application are highly decoupled from each other, and failure of one component
> doesn't mean the other parts will stop working. > doesn't mean the other parts will stop working.
Wikipedia says Wikipedia says
> Circuit breaker is a design pattern used in modern software development. It is used to detect > Circuit breaker is a design pattern used in modern software development. It is used to detect
> failures and encapsulates the logic of preventing a failure from constantly recurring, during > failures and encapsulates the logic of preventing a failure from constantly recurring, during
> maintenance, temporary external system failure or unexpected system difficulties. > maintenance, temporary external system failure or unexpected system difficulties.
## Programmatic Example ## Programmatic Example
So, how does this all come together? With the above example in mind we will imitate the So, how does this all come together? With the above example in mind we will imitate the
functionality in a simple example. A monitoring service mimics the web app and makes both local and functionality in a simple example. A monitoring service mimics the web app and makes both local and
remote calls. remote calls.
The service architecture is as follows: The service architecture is as follows:
@@ -55,6 +58,7 @@ The service architecture is as follows:
In terms of code, the end user application is: In terms of code, the end user application is:
```java ```java
@Slf4j @Slf4j
public class App { public class App {
@@ -114,7 +118,7 @@ public class App {
} }
``` ```
The monitoring service: The monitoring service:
```java ```java
public class MonitoringService { public class MonitoringService {
@@ -160,147 +164,154 @@ public class MonitoringService {
} }
} }
``` ```
As it can be seen, it does the call to get local resources directly, but it wraps the call to
As it can be seen, it does the call to get local resources directly, but it wraps the call to
remote (costly) service in a circuit breaker object, which prevents faults as follows: remote (costly) service in a circuit breaker object, which prevents faults as follows:
```java ```java
public class DefaultCircuitBreaker implements CircuitBreaker { public class DefaultCircuitBreaker implements CircuitBreaker {
private final long timeout; private final long timeout;
private final long retryTimePeriod; private final long retryTimePeriod;
private final RemoteService service; private final RemoteService service;
long lastFailureTime; long lastFailureTime;
private String lastFailureResponse; private String lastFailureResponse;
int failureCount; int failureCount;
private final int failureThreshold; private final int failureThreshold;
private State state; private State state;
// Future time offset, in nanoseconds // Future time offset, in nanoseconds
private final long futureTime = 1_000_000_000_000L; private final long futureTime = 1_000_000_000_000L;
/** /**
* Constructor to create an instance of Circuit Breaker. * Constructor to create an instance of Circuit Breaker.
* *
* @param timeout Timeout for the API request. Not necessary for this simple example * @param timeout Timeout for the API request. Not necessary for this simple example
* @param failureThreshold Number of failures we receive from the depended service before changing * @param failureThreshold Number of failures we receive from the depended service before changing
* state to 'OPEN' * state to 'OPEN'
* @param retryTimePeriod Time period after which a new request is made to remote service for * @param retryTimePeriod Time period after which a new request is made to remote service for
* status check. * status check.
*/ */
DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold, DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold,
long retryTimePeriod) { long retryTimePeriod) {
this.service = serviceToCall; this.service = serviceToCall;
// We start in a closed state hoping that everything is fine // We start in a closed state hoping that everything is fine
this.state = State.CLOSED; this.state = State.CLOSED;
this.failureThreshold = failureThreshold; this.failureThreshold = failureThreshold;
// Timeout for the API request. // Timeout for the API request.
// Used to break the calls made to remote resource if it exceeds the limit // Used to break the calls made to remote resource if it exceeds the limit
this.timeout = timeout; this.timeout = timeout;
this.retryTimePeriod = retryTimePeriod; this.retryTimePeriod = retryTimePeriod;
//An absurd amount of time in future which basically indicates the last failure never happened //An absurd amount of time in future which basically indicates the last failure never happened
this.lastFailureTime = System.nanoTime() + futureTime; this.lastFailureTime = System.nanoTime() + futureTime;
this.failureCount = 0; this.failureCount = 0;
}
// Reset everything to defaults
@Override
public void recordSuccess() {
this.failureCount = 0;
this.lastFailureTime = System.nanoTime() + futureTime;
this.state = State.CLOSED;
}
@Override
public void recordFailure(String response) {
failureCount = failureCount + 1;
this.lastFailureTime = System.nanoTime();
// Cache the failure response for returning on open state
this.lastFailureResponse = response;
}
// Evaluate the current state based on failureThreshold, failureCount and lastFailureTime.
protected void evaluateState() {
if (failureCount >= failureThreshold) { //Then something is wrong with remote service
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
//We have waited long enough and should try checking if service is up
state = State.HALF_OPEN;
} else {
//Service would still probably be down
state = State.OPEN;
}
} else {
//Everything is working fine
state = State.CLOSED;
} }
}
// Reset everything to defaults @Override
@Override public String getState() {
public void recordSuccess() { evaluateState();
this.failureCount = 0; return state.name();
this.lastFailureTime = System.nanoTime() + futureTime; }
this.state = State.CLOSED;
}
@Override /**
public void recordFailure(String response) { * Break the circuit beforehand if it is known service is down Or connect the circuit manually if
failureCount = failureCount + 1; * service comes online before expected.
*
* @param state State at which circuit is in
*/
@Override
public void setState(State state) {
this.state = state;
switch (state) {
case OPEN -> {
this.failureCount = failureThreshold;
this.lastFailureTime = System.nanoTime(); this.lastFailureTime = System.nanoTime();
// Cache the failure response for returning on open state }
this.lastFailureResponse = response; case HALF_OPEN -> {
this.failureCount = failureThreshold;
this.lastFailureTime = System.nanoTime() - retryTimePeriod;
}
default -> this.failureCount = 0;
} }
}
// Evaluate the current state based on failureThreshold, failureCount and lastFailureTime. /**
protected void evaluateState() { * Executes service call.
if (failureCount >= failureThreshold) { //Then something is wrong with remote service *
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) { * @return Value from the remote resource, stale response or a custom exception
//We have waited long enough and should try checking if service is up */
state = State.HALF_OPEN; @Override
} else { public String attemptRequest() throws RemoteServiceException {
//Service would still probably be down evaluateState();
state = State.OPEN; if (state == State.OPEN) {
} // return cached response if the circuit is in OPEN state
} else { return this.lastFailureResponse;
//Everything is working fine } else {
state = State.CLOSED; // Make the API request if the circuit is not OPEN
} try {
} //In a real application, this would be run in a thread and the timeout
//parameter of the circuit breaker would be utilized to know if service
@Override //is working. Here, we simulate that based on server response itself
public String getState() { var response = service.call();
evaluateState(); // Yay!! the API responded fine. Let's reset everything.
return state.name(); recordSuccess();
} return response;
} catch (RemoteServiceException ex) {
/** recordFailure(ex.getMessage());
* Break the circuit beforehand if it is known service is down Or connect the circuit manually if throw ex;
* service comes online before expected. }
*
* @param state State at which circuit is in
*/
@Override
public void setState(State state) {
this.state = state;
switch (state) {
case OPEN -> {
this.failureCount = failureThreshold;
this.lastFailureTime = System.nanoTime();
}
case HALF_OPEN -> {
this.failureCount = failureThreshold;
this.lastFailureTime = System.nanoTime() - retryTimePeriod;
}
default -> this.failureCount = 0;
}
}
/**
* Executes service call.
*
* @return Value from the remote resource, stale response or a custom exception
*/
@Override
public String attemptRequest() throws RemoteServiceException {
evaluateState();
if (state == State.OPEN) {
// return cached response if the circuit is in OPEN state
return this.lastFailureResponse;
} else {
// Make the API request if the circuit is not OPEN
try {
//In a real application, this would be run in a thread and the timeout
//parameter of the circuit breaker would be utilized to know if service
//is working. Here, we simulate that based on server response itself
var response = service.call();
// Yay!! the API responded fine. Let's reset everything.
recordSuccess();
return response;
} catch (RemoteServiceException ex) {
recordFailure(ex.getMessage());
throw ex;
}
}
} }
}
} }
``` ```
How does the above pattern prevent failures? Let's understand via this finite state machine How does the above pattern prevent failures? Let's understand via this finite state machine
implemented by it. implemented by it.
![alt text](./etc/StateDiagram.png "State Diagram") ![alt text](./etc/StateDiagram.png "State Diagram")
- We initialize the Circuit Breaker object with certain parameters: `timeout`, `failureThreshold` and `retryTimePeriod` which help determine how resilient the API is. - We initialize the Circuit Breaker object with certain parameters: `timeout`, `failureThreshold` and `retryTimePeriod`
which help determine how resilient the API is.
- Initially, we are in the `closed` state and nos remote calls to the API have occurred. - Initially, we are in the `closed` state and nos remote calls to the API have occurred.
- Every time the call succeeds, we reset the state to as it was in the beginning. - Every time the call succeeds, we reset the state to as it was in the beginning.
- If the number of failures cross a certain threshold, we move to the `open` state, which acts just like an open circuit and prevents remote service calls from being made, thus saving resources. (Here, we return the response called ```stale response from API```) - If the number of failures cross a certain threshold, we move to the `open` state, which acts just like an open circuit
- Once we exceed the retry timeout period, we move to the `half-open` state and make another call to the remote service again to check if the service is working so that we can serve fresh content. A failure sets it back to `open` state and another attempt is made after retry timeout period, while a success sets it to `closed` state so that everything starts working normally again. and prevents remote service calls from being made, thus saving resources. (Here, we return the response
called ```stale response from API```)
- Once we exceed the retry timeout period, we move to the `half-open` state and make another call to the remote service
again to check if the service is working so that we can serve fresh content. A failure sets it back to `open` state
and another attempt is made after retry timeout period, while a success sets it to `closed` state so that everything
starts working normally again.
## Class diagram ## Class diagram
@@ -330,14 +341,18 @@ Benefits:
Trade-Offs: Trade-Offs:
* The complexity of the system increases as the pattern requires additional logic to detect failures and manage the state of the circuit breaker * The complexity of the system increases as the pattern requires additional logic to detect failures and manage the
* May lead to system degradation if not properly configured, as legitimate requests might be blocked if the circuit is open state of the circuit breaker
* May lead to system degradation if not properly configured, as legitimate requests might be blocked if the circuit is
open
* Requires careful tuning of thresholds and timeout periods to balance between responsiveness and protection * Requires careful tuning of thresholds and timeout periods to balance between responsiveness and protection
## Related Patterns ## Related Patterns
- [Retry Pattern](https://github.com/iluwatar/java-design-patterns/tree/master/retry): Can be used in conjunction with the Circuit Breaker pattern to retry failed operations before opening the circuit - [Retry Pattern](https://github.com/iluwatar/java-design-patterns/tree/master/retry): Can be used in conjunction with
- [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead): Can be used to isolate different parts of the system to prevent failures from spreading across the system the Circuit Breaker pattern to retry failed operations before opening the circuit
- [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead): Can be used to isolate
different parts of the system to prevent failures from spreading across the system
## Credits ## Credits
+32 -11
View File
@@ -13,21 +13,35 @@ tags:
## Intent ## Intent
The Client Session design pattern aims to maintain a user's state and data across multiple requests within a web application, ensuring a continuous and personalized user experience. The Client Session design pattern aims to maintain a user's state and data across multiple requests within a web
application, ensuring a continuous and personalized user experience.
## Explanation ## Explanation
Real-World Example Real-World Example
> You're looking to create a data management app allowing users to send requests to the server to modify and make changes to data stored on their devices. These requests are small and the data is individual to each user, negating the need for a large scale database implementation. Using the client session pattern, you are able to handle multiple concurrent requests, load balancing clients across different servers with ease due to servers remaining stateless. You also remove the need to store session IDs on the server side due to clients providing all the information that a server needs to perform their process. > You're looking to create a data management app allowing users to send requests to the server to modify and make
> changes to data stored on their devices. These requests are small and the data is individual to each user, negating the
> need for a large scale database implementation. Using the client session pattern, you are able to handle multiple
> concurrent requests, load balancing clients across different servers with ease due to servers remaining stateless. You
> also remove the need to store session IDs on the server side due to clients providing all the information that a server
> needs to perform their process.
In Plain words In Plain words
> Instead of storing information about the current client and the information being accessed on the server, it is maintained client side only. Client has to send session data with each request to the server and has to send an updated state back to the client, which is stored on the clients machine. The server doesn't have to store the client information. ([ref](https://dzone.com/articles/practical-php-patterns/practical-php-patterns-client)) > Instead of storing information about the current client and the information being accessed on the server, it is
> maintained client side only. Client has to send session data with each request to the server and has to send an updated
> state back to the client, which is stored on the clients machine. The server doesn't have to store the client
> information. ([ref](https://dzone.com/articles/practical-php-patterns/practical-php-patterns-client))
**Programmatic Example** **Programmatic Example**
Here is the sample code to describe the client-session pattern. In the below code we are first creating an instance of the Server. This server instance will then be used to get Session objects for two clients. As you can see from the code below the Session object can be used to store any relevant information that are required by the server to process the client request. These session objects will then be passed on with every Request to the server. The Request will have the Session object that stores the relevant client details along with the required data for processing the request. The session information in every request helps the server identify the client and process the request accordingly. Here is the sample code to describe the client-session pattern. In the below code we are first creating an instance of
the Server. This server instance will then be used to get Session objects for two clients. As you can see from the code
below the Session object can be used to store any relevant information that are required by the server to process the
client request. These session objects will then be passed on with every Request to the server. The Request will have the
Session object that stores the relevant client details along with the required data for processing the request. The
session information in every request helps the server identify the client and process the request accordingly.
```java ```java
public class App { public class App {
@@ -93,20 +107,27 @@ Use the client state pattern when:
Benefits: Benefits:
* Improved server performance by reducing the need to store user state on the server. * Improved server performance by reducing the need to store user state on the server.
* Enhanced user experience through personalized content and seamless navigation across different parts of the application. * Enhanced user experience through personalized content and seamless navigation across different parts of the
application.
* Flexibility in managing sessions through various client-side storage mechanisms (e.g., cookies, Web Storage API). * Flexibility in managing sessions through various client-side storage mechanisms (e.g., cookies, Web Storage API).
Trade-offs: Trade-offs:
* Potential security risks if sensitive information is stored in client sessions without proper encryption and validation. * Potential security risks if sensitive information is stored in client sessions without proper encryption and
* Dependence on client-side capabilities and settings, such as cookie policies, which can vary across browsers and user configurations. validation.
* Increased complexity in session management logic, especially in handling session expiration, renewal, and synchronization across multiple devices or tabs. * Dependence on client-side capabilities and settings, such as cookie policies, which can vary across browsers and user
configurations.
* Increased complexity in session management logic, especially in handling session expiration, renewal, and
synchronization across multiple devices or tabs.
## Related Patterns ## Related Patterns
* Server Session: Often used in conjunction with the Client Session pattern to provide a balance between client-side efficiency and server-side control. * Server Session: Often used in conjunction with the Client Session pattern to provide a balance between client-side
* [Singleton](https://java-design-patterns.com/patterns/singleton/): Ensuring a single instance of a user's session throughout the application. efficiency and server-side control.
* [State](https://java-design-patterns.com/patterns/state/): Managing state transitions in a session, such as authenticated, guest, or expired states. * [Singleton](https://java-design-patterns.com/patterns/singleton/): Ensuring a single instance of a user's session
throughout the application.
* [State](https://java-design-patterns.com/patterns/state/): Managing state transitions in a session, such as
authenticated, guest, or expired states.
## Credits ## Credits
+80 -36
View File
@@ -1,44 +1,59 @@
--- ---
title: Collecting Parameter title: Collecting Parameter
category: Idiom category: Behavioral
language: en language: en
tag: tag:
- Generic - Accumulation
- Generic
--- ---
## Name ## Also known as
Collecting Parameter
* Collector
* Accumulator
## Intent ## Intent
To store the collaborative result of numerous methods within a collection.
Aims to simplify methods that collect information by passing a single collection object through various method calls,
allowing them to add results to this collection rather than each method creating its own collection.
## Explanation ## Explanation
### Real-world example ### Real-world example
Within a large corporate building, there exists a global printer queue that is a collection of all the printing jobs Within a large corporate building, there exists a global printer queue that is a collection of all the printing jobs
that are currently pending. Various floors contain different models of printers, each having a different printing that are currently pending. Various floors contain different models of printers, each having a different printing
policy. We must construct a program that can continually add appropriate printing jobs to a collection, which is called the *collecting parameter*. policy. We must construct a program that can continually add appropriate printing jobs to a collection, which is called
the *collecting parameter*.
### In plain words ### In plain words
Instead of having one giant method that contains numerous policies for collecting information into a variable, we can Instead of having one giant method that contains numerous policies for collecting information into a variable, we can
create numerous smaller functions that each take parameter, and append new information. We can pass the parameter to create numerous smaller functions that each take parameter, and append new information. We can pass the parameter to all
all of these smaller functions and by the end, we will have what we wanted originally. This time, the code is cleaner of these smaller functions and by the end, we will have what we wanted originally. This time, the code is cleaner and
and easier to understand. Because the larger function has been broken down, the code is also easier to modify as changes easier to understand. Because the larger function has been broken down, the code is also easier to modify as changes are
are localised to the smaller functions. localised to the smaller functions.
### Wikipedia says ### Wikipedia says
In the Collecting Parameter idiom a collection (list, map, etc.) is passed repeatedly as a parameter to a method which adds items to the collection.
In the Collecting Parameter idiom a collection (list, map, etc.) is passed repeatedly as a parameter to a method which
adds items to the collection.
### Programmatic example ### Programmatic example
Coding our example from above, we may use the collection `result` as a collecting parameter. The following restrictions Coding our example from above, we may use the collection `result` as a collecting parameter. The following restrictions
are implemented: are implemented:
- If an A4 paper is coloured, it must also be single-sided. All other non-coloured papers are accepted - If an A4 paper is coloured, it must also be single-sided. All other non-coloured papers are accepted
- A3 papers must be non-coloured and single-sided - A3 papers must be non-coloured and single-sided
- A2 papers must be single-page, single-sided, and non-coloured - A2 papers must be single-page, single-sided, and non-coloured
```java ```java
package com.iluwatar.collectingparameter; package com.iluwatar.collectingparameter;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Queue; import java.util.Queue;
public class App { public class App {
static PrinterQueue printerQueue = PrinterQueue.getInstance(); static PrinterQueue printerQueue = PrinterQueue.getInstance();
@@ -57,10 +72,10 @@ public class App {
/* /*
This variable is the collecting parameter. This variable is the collecting parameter.
*/ */
var result = new LinkedList<PrinterItem>(); var result = new LinkedList<PrinterItem>();
/* /*
* Using numerous sub-methods to collaboratively add information to the result collecting parameter * Using numerous sub-methods to collaboratively add information to the result collecting parameter
*/ */
addA4Papers(result); addA4Papers(result);
@@ -76,6 +91,7 @@ appropriate print jobs as per the policy described previously. The three policie
```java ```java
public class App { public class App {
static PrinterQueue printerQueue = PrinterQueue.getInstance(); static PrinterQueue printerQueue = PrinterQueue.getInstance();
/** /**
* Adds A4 document jobs to the collecting parameter according to some policy that can be whatever the client * Adds A4 document jobs to the collecting parameter according to some policy that can be whatever the client
* (the print center) wants. * (the print center) wants.
@@ -132,7 +148,7 @@ public class App {
// Encoding the policy into a Boolean: the A2 paper must be single page, single-sided, and non-coloured. // Encoding the policy into a Boolean: the A2 paper must be single page, single-sided, and non-coloured.
var isNotColouredSingleSidedAndOnePage = nextItem.pageCount == 1 && !nextItem.isDoubleSided var isNotColouredSingleSidedAndOnePage = nextItem.pageCount == 1 && !nextItem.isDoubleSided
&& !nextItem.isColour; && !nextItem.isColour;
if (isNotColouredSingleSidedAndOnePage) { if (isNotColouredSingleSidedAndOnePage) {
printerItemsCollection.add(nextItem); printerItemsCollection.add(nextItem);
} }
@@ -142,8 +158,9 @@ public class App {
} }
``` ```
Each method takes a collecting parameter as an argument. It then adds elements, taken from a global variable, Each method takes a collecting parameter as an argument. It then adds elements, taken from a global variable, to this
to this collecting parameter if each element satisfies a given criteria. These methods can have whatever policy the client desires. collecting parameter if each element satisfies a given criteria. These methods can have whatever policy the client
desires.
In this programmatic example, three print jobs are added to the queue. Only the first two print jobs should be added to In this programmatic example, three print jobs are added to the queue. Only the first two print jobs should be added to
the collecting parameter as per the policy. The elements of the `result` variable after execution are, the collecting parameter as per the policy. The elements of the `result` variable after execution are,
@@ -156,42 +173,69 @@ the collecting parameter as per the policy. The elements of the `result` variabl
which is what we expected. which is what we expected.
## Class diagram ## Class diagram
![alt text](./etc/collecting-parameter.urm.png "Collecting Parameter") ![alt text](./etc/collecting-parameter.urm.png "Collecting Parameter")
## Applicability ## Applicability
Use the Collecting Parameter design pattern when Use the Collecting Parameter design pattern when
- you want to return a collection or object that is the collaborative result of several methods
- You want to simplify a method that accumulates data as the original method is too complex - When multiple methods produce a collection of results and you want to aggregate these results in a unified manner.
- In scenarios where reducing the number of collections created by methods can improve memory efficiency and
performance.
- When you're refactoring large methods that perform multiple tasks, including the collection of results from various
operations.
## Tutorials ## Tutorials
Tutorials for this method are found in: Tutorials for this method are found in:
- [Refactoring To Patterns](http://www.tarrani.net/RefactoringToPatterns.pdf) by Joshua Kerivsky - [Refactoring To Patterns](http://www.tarrani.net/RefactoringToPatterns.pdf) by Joshua Kerivsky
- [Smalltalk Best Practice Patterns](https://ptgmedia.pearsoncmg.com/images/9780134769042/samplepages/013476904X.pdf) by Kent Beck - [Smalltalk Best Practice Patterns](https://ptgmedia.pearsoncmg.com/images/9780134769042/samplepages/013476904X.pdf) by
Kent Beck
## Known uses ## Known uses
Joshua Kerivsky gives a real-world example in his book 'Refactoring to Patterns'. He gives an example of using the Joshua Kerivsky gives a real-world example in his book 'Refactoring to Patterns'. He gives an example of using the
Collecting Parameter Design Pattern to create a `toString()` method for an XML tree. Without using this design pattern, Collecting Parameter Design Pattern to create a `toString()` method for an XML tree. Without using this design pattern,
this would require a bulky function with conditionals and concatenation that would worsen code readability. Such a method this would require a bulky function with conditionals and concatenation that would worsen code readability. Such a
can be broken down into smaller methods, each appending their own set of information to the collecting parameter. method can be broken down into smaller methods, each appending their own set of information to the collecting parameter.
See this in [Refactoring To Patterns](http://www.tarrani.net/RefactoringToPatterns.pdf). See this in [Refactoring To Patterns](http://www.tarrani.net/RefactoringToPatterns.pdf).
## Consequences Other examples include:
Pros:
- Makes code more readable
- Avoids 'linkages', where numerous methods reference the same global variable
- Increases maintainability by decomposing larger functions
Cons: - Aggregating error messages or validation failures across a complex validation process.
- May increase code length - Collecting elements or information while traversing a complex data structure.
- Adds 'layers' of methods - Refactoring complex reporting functionalities where various parts of a report are generated by different methods.
## Consequences
Benefits:
- Reduces code duplication by centralizing collection handling in a single place.
- Enhances clarity and maintainability by making it explicit where and how results are collected.
- Improves performance by minimizing the creation and management of multiple collection objects.
Trade-offs:
- Increases coupling between the caller and the methods being called since they must agree on the collection to use.
- May introduce side effects in methods if not carefully managed, as methods are no longer self-contained in their
result handling.
## Related patterns ## Related patterns
- [Compose Methods](https://www.geeksforgeeks.org/composite-design-pattern/)
- [Composite](https://java-design-patterns.com/patterns/composite/): Can be used in tandem with Collecting Parameter
when dealing with hierarchical structures, allowing results to be collected across a composite structure.
- [Visitor](https://java-design-patterns.com/patterns/visitor/): Often used together, where Visitor handles traversal
and operations on a structure, and Collecting Parameter accumulates the results.
- [Command](https://java-design-patterns.com/patterns/command/): Commands may utilize Collecting Parameter to aggregate
results from multiple operations executed by the command objects.
## Credits ## Credits
Following books were used:
- [Refactoring To Patterns](http://www.tarrani.net/RefactoringToPatterns.pdf) by Joshua Kerivsky - [Refactoring To Patterns](http://www.tarrani.net/RefactoringToPatterns.pdf) by Joshua Kerivsky
- [Smalltalk Best Practice Patterns](https://ptgmedia.pearsoncmg.com/images/9780134769042/samplepages/013476904X.pdf) by Kent Beck - [Smalltalk Best Practice Patterns](https://ptgmedia.pearsoncmg.com/images/9780134769042/samplepages/013476904X.pdf) by
Sites: Kent Beck
- [Wiki](https://wiki.c2.com/?CollectingParameter) - [Wiki](https://wiki.c2.com/?CollectingParameter)
- [Refactoring: Improving the Design of Existing Code](https://amzn.to/3TVEgaB)
- [Clean Code: A Handbook of Agile Software Craftsmanship](https://amzn.to/4aApLP0)
@@ -27,8 +27,6 @@ package com.iluwatar.collectingparameter;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.Timeout;
import java.util.LinkedList;
import java.util.Queue;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;