Skip to content

Commit

Permalink
docs: updates to several patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
iluwatar committed May 28, 2024
1 parent 4652842 commit 584e949
Show file tree
Hide file tree
Showing 70 changed files with 344 additions and 476 deletions.
2 changes: 1 addition & 1 deletion abstract-document/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The Abstract Document design pattern is a structural design pattern that aims to

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

> Imagine a library system where books can have different formats and attributes: physical books, eBooks, and audiobooks. Each format has unique properties, such as page count for physical books, file size for eBooks, and duration for audiobooks. The Abstract Document design pattern allows the library system to manage these diverse formats flexibly. By using this pattern, the system can store and retrieve properties dynamically, without needing a rigid structure for each book type, making it easier to add new formats or attributes in the future without significant changes to the codebase.
Expand Down
7 changes: 3 additions & 4 deletions active-object/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,20 @@ public abstract class ActiveCreature {
}
```

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:

```java
public class Orc extends ActiveCreature {

public Orc(String name) {
super(name);
}

}
```

Now, we can create multiple creatures such as Orcs, tell them to eat and roam, and they will execute it on their own thread of control:
Now, we can create multiple creatures such as orcs, tell them to eat and roam, and they will execute it on their own thread of control:

```java
public class App implements Runnable {
Expand Down
2 changes: 1 addition & 1 deletion acyclic-visitor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The Acyclic Visitor pattern decouples operations from an object hierarchy, allow

## Explanation

Real world example
Real-world example

> An analogous real-world example of the Acyclic Visitor pattern is a museum guide system. Imagine a museum with various exhibits like paintings, sculptures, and historical artifacts. The museum has different types of guides (audio guide, human guide, virtual reality guide) that provide information about each exhibit. Instead of modifying the exhibits every time a new guide type is introduced, each guide implements an interface to visit different exhibit types. This way, the museum can add new types of guides without altering the existing exhibits, ensuring that the system remains extensible and maintainable without forming any dependency cycles.
Expand Down
4 changes: 0 additions & 4 deletions adapter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,6 @@ The program outputs:
10:25:08.074 [main] INFO com.iluwatar.adapter.FishingBoat -- The fishing boat is sailing
```

## Class diagram

![Adapter](./etc/adapter.urm.png "Adapter class diagram")

## Applicability

Use the Adapter pattern when
Expand Down
6 changes: 1 addition & 5 deletions aggregator-microservices/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ To aggregate responses from multiple microservices and return a consolidated res

## Explanation

Real world example
Real-world example

> Imagine an online travel booking platform. When a user searches for a vacation package, the platform needs to gather information from several different services: flights, hotels, car rentals, and local attractions. Instead of the user making separate requests to each service, the platform employs an Aggregator Microservice. This microservice calls each of these services, collects their responses, and then consolidates the information into a single, unified response that is sent back to the user. This simplifies the user experience by providing all necessary travel details in one place and reduces the number of direct interactions the user needs to have with the underlying services.
Expand Down Expand Up @@ -98,10 +98,6 @@ curl http://localhost:50004/product
{"title":"The Product Title.","productInventories":5}
```

## Class diagram

![Class diagram of the Aggregator Microservices Pattern](./aggregator-service/etc/aggregator-service.png "Aggregator Microservice")

## 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.
Expand Down
6 changes: 1 addition & 5 deletions ambassador/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Provide a helper service instance on a client and offload common functionality a

## Explanation

Real world example
Real-world example

> Imagine a busy hotel where guests frequently request restaurant reservations, event tickets, or transportation arrangements. Instead of each guest individually contacting these services, the hotel provides a concierge. The concierge handles these tasks on behalf of the guests, ensuring that reservations are made smoothly, tickets are booked on time, and transportation is scheduled efficiently.
>
Expand Down Expand Up @@ -175,10 +175,6 @@ Failed to reach remote:(3)
Service result:-1
```

## Class diagram

![Ambassador](./etc/ambassador.urm.png "Ambassador class diagram")

## Applicability

* Cloud Native and Microservices Architectures: Especially useful in distributed systems where it's crucial to monitor, log, and secure inter-service communication.
Expand Down
4 changes: 0 additions & 4 deletions anti-corruption-layer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,6 @@ public class LegacyShop {
}
```

## Class diagram

![Anti-Corruption Layer](./etc/anti-corruption-layer.urm.png "Anti-Corruption Layer class diagram")

## Applicability

Use this pattern when:
Expand Down
6 changes: 1 addition & 5 deletions api-gateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The API Gateway design pattern aims to provide a unified interface to a set of m

## Explanation

Real world example
Real-world example

> In a large e-commerce platform, an API Gateway is used as the single entry point for all client requests. When a user visits the site or uses the mobile app, their requests for product information, user authentication, order processing, and payment are all routed through the API Gateway. The gateway handles tasks such as user authentication, rate limiting to prevent abuse, and logging for monitoring purposes. This setup simplifies the client interface and ensures that all backend microservices, like the product catalog service, user service, order service, and payment service, can evolve independently without affecting the client directly. This also enhances security by providing a centralized point to enforce policies and monitor traffic.
Expand Down Expand Up @@ -125,10 +125,6 @@ public class ApiGateway {
}
```

## Class diagram

![API Gateway](./etc/api-gateway.png "API Gateway")

## Applicability

* When building a microservices architecture, and there's a need to abstract the complexity of microservices from the client.
Expand Down
4 changes: 0 additions & 4 deletions async-method-invocation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,6 @@ Here's the program console output.
21:47:08.618[main]INFO com.iluwatar.async.method.invocation.App-Space rocket<50>launch complete
```

# Class diagram

![Async Method Invocation](./etc/async-method-invocation.urm.png "Async Method Invocation")

## Applicability

Use the async method invocation pattern when
Expand Down
8 changes: 2 additions & 6 deletions balking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Balking Pattern is used to prevent an object from executing a certain code if it

## Explanation

Real world example
Real-world example

> A real-world analogy of the Balking design pattern can be seen in a laundry service. Imagine a washing machine at a laundromat that only starts washing clothes if the door is properly closed and locked. If a user tries to start the machine while the door is open, the machine balks and does nothing. This ensures that the washing process only begins when it is safe to do so, preventing water spillage and potential damage to the machine. Similarly, the Balking pattern in software design ensures that operations are only executed when the object is in an appropriate state, preventing erroneous actions and maintaining system stability.
Expand All @@ -31,7 +31,7 @@ Wikipedia says

There's a start-button in a washing machine to initiate the laundry washing. When the washing machine is inactive the button works as expected, but if it's already washing the button does nothing.

In this example implementation, `WashingMachine` is an object that has two states in which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING using a thread-safe method. On the other hand, if it already has been washing and any other thread executes `wash()`it won't do that and returns without doing anything.
In this example implementation, `WashingMachine` is an object that has two states in which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING using a thread-safe method. On the other hand, if it already has been washing and any other thread executes `wash`it won't do that and returns without doing anything.

Here are the relevant parts of the `WashingMachine` class.

Expand Down Expand Up @@ -114,10 +114,6 @@ Here is the console output of the program.
14:02:52.324 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed.
```

## Class diagram

![Balking](./etc/balking.png "Balking")

## Applicability

Use the Balking pattern when
Expand Down
4 changes: 2 additions & 2 deletions bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Wikipedia says
**Programmatic 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.
Imagine you have a weapon that can have various enchantments, and you need to combine different weapons with different enchantments. How would you handle this? Would you create multiple copies of each weapon, each with a different enchantment, or would you create separate enchantments and apply them to the weapon as needed? The Bridge pattern enables you to do the latter.

Here we have the `Weapon` hierarchy:

Expand Down Expand Up @@ -116,7 +116,7 @@ public class Hammer implements Weapon {
}
```

Here's the separate enchantment hierarchy:
Here's the separate `Enchantment` hierarchy:

```java
public interface Enchantment {
Expand Down
10 changes: 5 additions & 5 deletions builder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,21 @@ Wikipedia says

> The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor antipattern.
That said, let's clarify what telescoping constructor antipattern is. At one point or the other, we have all seen a constructor like below:
With that in mind, let's explain what the telescoping constructor antipattern is. At some point, we have all encountered a constructor like the one below:

```java
public Hero(Profession profession,String name,HairType hairType,HairColor hairColor,Armor armor,Weapon weapon){
// Value assignments
}
```

As you can see the number of constructor parameters can quickly get out of hand, and it may become difficult to understand the arrangement of parameters. Plus this parameter list could keep on growing if you would want to add more options in the future. This is called telescoping constructor antipattern.
As you can see, the number of constructor parameters can quickly become overwhelming, making it difficult to understand their arrangement. Additionally, this list of parameters might continue to grow if you decide to add more options in the future. This is known as the telescoping constructor antipattern.

**Programmatic Example**

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 simplest option is to let the computer generate the character for you. However, if you prefer to manually select character details such as profession, gender, hair color, etc., the character creation becomes a step-by-step process that concludes once all selections are made.

The sane alternative is to use the Builder pattern. First of all, we have our hero that we want to create:
A more sensible approach is to use the Builder pattern. First, let's consider the `Hero` that we want to create:

```java
public final class Hero {
Expand All @@ -62,7 +62,7 @@ public final class Hero {
}
```

Then we have the builder:
Then we have the `Builder`:

```java
public static class Builder {
Expand Down
2 changes: 1 addition & 1 deletion bytecode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Allows encoding behavior as instructions for a virtual machine.

## Explanation

Real world example
Real-world example

> An analogous real-world example of the Bytecode design pattern can be seen in the process of translating a book into multiple languages. Instead of directly translating the book from the original language into every other language, the book is first translated into a common intermediate language, like Esperanto. This intermediate version is easier to translate because it is simpler and more structured. Translators for each target language then translate from Esperanto into their specific languages. This approach ensures consistency, reduces errors, and simplifies the translation process, similar to how bytecode serves as an intermediate representation to optimize and facilitate the execution of high-level programming languages across different platforms.
Expand Down
6 changes: 1 addition & 5 deletions caching/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ In plain words

> Caching pattern keeps frequently needed data in fast-access storage to improve performance.
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.
Expand Down Expand Up @@ -419,10 +419,6 @@ UserAccount(userId=003, userName=Adam, additionalInfo=He likes food.)
17:00:56.314 [Thread-0] INFO com.iluwatar.caching.CacheStore -- # flushCache...
```

## Class diagram

![Caching](./etc/caching.png "Caching")

## Applicability

Use the Caching pattern when
Expand Down
6 changes: 3 additions & 3 deletions callback/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Callback is a piece of executable code that is passed as an argument to other co

## Explanation

Real world example
Real-world example

> A real-world analogy for the Callback design pattern can be found in the restaurant industry. Imagine a situation where you place an order at a busy restaurant. Instead of waiting at the counter for your food to be ready, you provide the cashier with your phone number. Once your order is prepared, the kitchen staff calls or sends a text message to notify you that your meal is ready for pickup.
>
Expand All @@ -39,7 +39,7 @@ Wikipedia says

We need to be notified after the executing task has finished. We pass a callback method for the executor and wait for it to call back on us.

Callback is a simple interface with single method.
`Callback` is a simple interface with single method.

```java
public interface Callback {
Expand All @@ -48,7 +48,7 @@ public interface Callback {
}
```

Next we define a task that will execute the callback after the task execution has finished.
Next we define `Task` that will execute the callback after the task execution has finished.

```java
public abstract class Task {
Expand Down
4 changes: 2 additions & 2 deletions chain-of-responsibility/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public enum RequestType {
}
```

Next, we show the request handler hierarchy.
Next, we show the `RequestHandler` hierarchy.

```java
public interface RequestHandler {
Expand Down Expand Up @@ -109,7 +109,7 @@ public class OrcCommander implements RequestHandler {

```

The Orc King gives the orders and forms the chain.
The `OrcKing` gives the orders and forms the chain.

```java
public class OrcKing {
Expand Down
68 changes: 65 additions & 3 deletions circuit-breaker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,57 @@ LOGGER.info(monitoringService.delayedServiceResponse());
LOGGER.info(delayedServiceCircuitBreaker.getState());
```

6. **Full example**

```java
public static void main(String[] args) {

var serverStartTime = System.nanoTime();

var delayedService = new DelayedRemoteService(serverStartTime, 5);
var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2,
2000 * 1000 * 1000);

var quickService = new QuickRemoteService();
var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2,
2000 * 1000 * 1000);

//Create an object of monitoring service which makes both local and remote calls
var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
quickServiceCircuitBreaker);

//Fetch response from local resource
LOGGER.info(monitoringService.localResourceResponse());

//Fetch response from delayed service 2 times, to meet the failure threshold
LOGGER.info(monitoringService.delayedServiceResponse());
LOGGER.info(monitoringService.delayedServiceResponse());

//Fetch current state of delayed service circuit breaker after crossing failure threshold limit
//which is OPEN now
LOGGER.info(delayedServiceCircuitBreaker.getState());

//Meanwhile, the delayed service is down, fetch response from the healthy quick service
LOGGER.info(monitoringService.quickServiceResponse());
LOGGER.info(quickServiceCircuitBreaker.getState());

//Wait for the delayed service to become responsive
try {
LOGGER.info("Waiting for delayed service to become responsive");
Thread.sleep(5000);
} catch (InterruptedException e) {
LOGGER.error("An error occurred: ", e);
}
//Check the state of delayed circuit breaker, should be HALF_OPEN
LOGGER.info(delayedServiceCircuitBreaker.getState());

//Fetch response from delayed service, which should be healthy by now
LOGGER.info(monitoringService.delayedServiceResponse());
//As successful response is fetched, it should be CLOSED again.
LOGGER.info(delayedServiceCircuitBreaker.getState());
}
```

Summary of the example

- Initialize the Circuit Breaker with parameters: `timeout`, `failureThreshold`, and `retryTimePeriod`.
Expand All @@ -104,11 +155,22 @@ Summary of the example
- After the retry timeout, transition to the `half-open` state to test the service.
- On success in `half-open` state, transition back to `closed`. On failure, return to `open`.

This example demonstrates how the Circuit Breaker pattern can help maintain application stability and resilience by managing remote service failures.
Program output:

## Class diagram
```
16:59:19.767 [main] INFO com.iluwatar.circuitbreaker.App -- Local Service is working
16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- Delayed service is down
16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- Delayed service is down
16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- OPEN
16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- Quick Service is working
16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- CLOSED
16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- Waiting for delayed service to become responsive
16:59:24.779 [main] INFO com.iluwatar.circuitbreaker.App -- HALF_OPEN
16:59:24.780 [main] INFO com.iluwatar.circuitbreaker.App -- Delayed service is working
16:59:24.780 [main] INFO com.iluwatar.circuitbreaker.App -- CLOSED
```

![Circuit Breaker](./etc/circuit-breaker.urm.png "Circuit Breaker class diagram")
This example demonstrates how the Circuit Breaker pattern can help maintain application stability and resilience by managing remote service failures.

## Applicability

Expand Down
Loading

0 comments on commit 584e949

Please sign in to comment.