docs: updates to several patterns

This commit is contained in:
Ilkka Seppälä
2024-05-28 19:46:39 +03:00
parent 4652842c94
commit 584e949714
70 changed files with 347 additions and 479 deletions
+1 -1
View File
@@ -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.
+3 -4
View File
@@ -99,9 +99,9 @@ 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 {
@@ -109,11 +109,10 @@ 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 {
+1 -1
View File
@@ -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.
-4
View File
@@ -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
+1 -5
View File
@@ -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.
@@ -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.
+1 -5
View File
@@ -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.
>
@@ -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.
-4
View File
@@ -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:
+1 -5
View File
@@ -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.
@@ -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.
-4
View File
@@ -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
+2 -6
View File
@@ -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.
@@ -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.
@@ -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
+2 -2
View File
@@ -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:
@@ -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 {
+5 -5
View File
@@ -26,7 +26,7 @@ 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){
@@ -34,13 +34,13 @@ public Hero(Profession profession,String name,HairType hairType,HairColor hairCo
}
```
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 {
@@ -62,7 +62,7 @@ public final class Hero {
}
```
Then we have the builder:
Then we have the `Builder`:
```java
public static class Builder {
+1 -1
View File
@@ -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.
+1 -5
View File
@@ -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.
@@ -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
+3 -3
View File
@@ -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.
>
@@ -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 {
@@ -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 {
+2 -2
View File
@@ -67,7 +67,7 @@ public enum RequestType {
}
```
Next, we show the request handler hierarchy.
Next, we show the `RequestHandler` hierarchy.
```java
public interface RequestHandler {
@@ -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 {
+66 -4
View File
@@ -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`.
@@ -104,12 +155,23 @@ 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`.
Program output:
```
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
```
This example demonstrates how the Circuit Breaker pattern can help maintain application stability and resilience by managing remote service failures.
## Class diagram
![Circuit Breaker](./etc/circuit-breaker.urm.png "Circuit Breaker class diagram")
## Applicability
* In distributed systems where individual service failures can lead to cascading system-wide failures
+2 -4
View File
@@ -19,11 +19,11 @@ The Client Session design pattern aims to maintain a user's state and data acros
## Explanation
Real-World Example
Real-world example
> A real-world example of the Client Session pattern is a library membership system. When a member logs in, the system starts a session to track their borrowing activities. This session holds data such as the member's ID, current borrowed books, due dates, and any fines. As the member browses the catalog, borrows books, or returns them, the session maintains this stateful information, ensuring the member's interactions are consistent and personalized until they log out or the session expires. This approach helps the library system manage user-specific data efficiently across multiple interactions, providing a seamless and personalized experience for the members.
In Plain words
In plain words
> The Client Session pattern manages user-specific data across multiple requests within a web application to maintain continuity and personalization.
@@ -37,8 +37,6 @@ The Client Session design pattern is a behavioral design pattern that maintains
In the given code, we have a `Server` class and a `Session` class. The `Server` class represents the server that processes incoming requests and assigns sessions to clients. The `Session` class represents a session that is assigned to a client.
Here's a programmatic example of the Client Session design pattern using the given code:
```java
// The Server class represents the server that processes incoming requests and assigns sessions to clients.
public class Server {
+45 -95
View File
@@ -36,125 +36,75 @@ Wikipedia says
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 policy. We must construct a program that can continually add appropriate printing jobs to a collection, which is called the collecting parameter.
Coding our example from above, we may use the collection `result` as a collecting parameter. The following restrictions are implemented:
The following business rules are implemented:
* 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
* A2 papers must be single-page, single-sided, and non-coloured
```java
public class App {
static PrinterQueue printerQueue = PrinterQueue.getInstance();
public static void main(String[] args) {
// Initialising the printer queue with jobs
printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A4, 5, false, false));
printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A3, 2, false, false));
printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A2, 5, false, false));
// This variable is the collecting parameter.
var result = new LinkedList<PrinterItem>();
// Using numerous sub-methods to collaboratively add information to the result collecting parameter
addA4Papers(result);
addA3Papers(result);
addA2Papers(result);
}
}
```
We use the `addA4Paper`, `addA3Paper`, and `addA2Paper` methods to populate the `result` collecting parameter with the appropriate print jobs as per the policy described previously. The three policies are encoded below,
Let's see the implementation first and explain afterward.
```java
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
* (the print center) wants.
*
* @param printerItemsCollection the collecting parameter
*/
public static void addA4Papers(Queue<PrinterItem> printerItemsCollection) {
/*
Iterate through the printer queue, and add A4 papers according to the correct policy to the collecting parameter,
which is 'printerItemsCollection' in this case.
*/
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
if (nextItem.paperSize.equals(PaperSizes.A4)) {
var isColouredAndSingleSided =
nextItem.isColour && !nextItem.isDoubleSided;
if (isColouredAndSingleSided) {
printerItemsCollection.add(nextItem);
} else if (!nextItem.isColour) {
printerItemsCollection.add(nextItem);
}
}
public static void main(String[] args) {
printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A4, 5, false, false));
printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A3, 2, false, false));
printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A2, 5, false, false));
var result = new LinkedList<PrinterItem>();
addValidA4Papers(result);
addValidA3Papers(result);
addValidA2Papers(result);
}
public static void addValidA4Papers(Queue<PrinterItem> printerItemsCollection) {
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
if (nextItem.paperSize.equals(PaperSizes.A4)) {
var isColouredAndSingleSided = nextItem.isColour && !nextItem.isDoubleSided;
if (isColouredAndSingleSided || !nextItem.isColour) {
printerItemsCollection.add(nextItem);
}
}
}
}
/**
* Adds A3 document jobs to the collecting parameter according to some policy that can be whatever the client
* (the print center) wants. The code is similar to the 'addA4Papers' method. The code can be changed to accommodate
* the wants of the client.
*
* @param printerItemsCollection the collecting parameter
*/
public static void addA3Papers(Queue<PrinterItem> printerItemsCollection) {
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
if (nextItem.paperSize.equals(PaperSizes.A3)) {
// Encoding the policy into a Boolean: the A3 paper cannot be coloured and double-sided at the same time
var isNotColouredAndSingleSided =
!nextItem.isColour && !nextItem.isDoubleSided;
if (isNotColouredAndSingleSided) {
printerItemsCollection.add(nextItem);
}
}
public static void addValidA3Papers(Queue<PrinterItem> printerItemsCollection) {
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
if (nextItem.paperSize.equals(PaperSizes.A3)) {
var isNotColouredAndSingleSided = !nextItem.isColour && !nextItem.isDoubleSided;
if (isNotColouredAndSingleSided) {
printerItemsCollection.add(nextItem);
}
}
}
}
/**
* Adds A2 document jobs to the collecting parameter according to some policy that can be whatever the client
* (the print center) wants. The code is similar to the 'addA4Papers' method. The code can be changed to accommodate
* the wants of the client.
*
* @param printerItemsCollection the collecting parameter
*/
public static void addA2Papers(Queue<PrinterItem> printerItemsCollection) {
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
if (nextItem.paperSize.equals(PaperSizes.A2)) {
// 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
&& !nextItem.isColour;
if (isNotColouredSingleSidedAndOnePage) {
printerItemsCollection.add(nextItem);
}
}
public static void addValidA2Papers(Queue<PrinterItem> printerItemsCollection) {
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
if (nextItem.paperSize.equals(PaperSizes.A2)) {
var isNotColouredSingleSidedAndOnePage = nextItem.pageCount == 1 && !nextItem.isDoubleSided
&& !nextItem.isColour;
if (isNotColouredSingleSidedAndOnePage) {
printerItemsCollection.add(nextItem);
}
}
}
}
}
```
Each method takes a collecting parameter as an argument. It then adds elements, taken from a global variable, to this collecting parameter if each element satisfies a given criteria. These methods can have whatever policy the client desires.
This `App` class is the main entry point of the application. It uses the Collecting Parameter design pattern to filter print jobs based on certain policies.
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,
1. **Initialization**: The `printerQueue` is initialized with three print jobs of different paper sizes (A4, A3, A2).
| paperSize | pageCount | isDoubleSided | isColour |
|-----------|-----------|---------------|----------|
| A4 | 5 | false | false |
| A3 | 2 | false | false |
2. **Creating the Collecting Parameter**: A `LinkedList` named `result` is created to store the print jobs that meet the policy requirements.
which is what we expected.
3. **Adding Valid Jobs to the Collecting Parameter**: The `addValidA4Papers`, `addValidA3Papers`, and `addValidA2Papers` methods are called. These methods iterate over the `printerQueue` and add the print jobs that meet the policy requirements to the `result` list.
## Class diagram
![Collecting Parameter](./etc/collecting-parameter.urm.png "Collecting Parameter")
The `result` list, which is the collecting parameter, accumulates the valid print jobs as it is passed from method to method. This is the essence of the Collecting Parameter design pattern.
## Applicability
+1 -1
View File
@@ -49,7 +49,7 @@ public static List<String> getModelsAfter2000(List<Car> cars){
**Step 2: Grouping**
Next, we want to group the cars by their category. This is done using the `groupingBy()` collector.
Next, we want to group the cars by their category. This is done using the `groupingBy` collector.
```java
public static Map<Category, List<Car>> getGroupingOfCarsByCategory(List<Car> cars){
@@ -18,7 +18,7 @@ Command Query Responsibility Segregation aims to segregate the operations that m
## Explanation
Real world example
Real-world example
> Imagine a modern library where the tasks of borrowing and returning books (commands) are handled at the front desk, while the task of searching for books and reading them (queries) happens in the reading area. The front desk optimizes for transaction efficiency and record-keeping, ensuring books are properly checked in and out. Meanwhile, the reading area is optimized for comfort and accessibility, making it easy for readers to find and engage with the books. This separation improves the library's overall efficiency and user experience, much like the CQRS pattern enhances a software system's performance and maintainability.
@@ -34,27 +34,64 @@ Microsoft's documentation says
One way to implement the Command Query Responsibility Segregation (CQRS) pattern is to separate the read and write operations into different services.
1. Command Service: The `CommandServiceImpl` class is used for write operations. It provides methods to create authors and books, and to add books to authors. Here's a snippet of how it's used:
Let's see the code implementation first and explain how it works afterward.
```java
var commands = new CommandServiceImpl();
commands.authorCreated(AppConstants.E_EVANS, "Eric Evans", "evans@email.com");
commands.bookAddedToAuthor("Domain-Driven Design", 60.08, AppConstants.E_EVANS);
public static void main(String[] args) {
// Create Authors and Books using CommandService
var commands = new CommandServiceImpl();
commands.authorCreated(AppConstants.E_EVANS, "Eric Evans", "evans@email.com");
commands.authorCreated(AppConstants.J_BLOCH, "Joshua Bloch", "jBloch@email.com");
commands.authorCreated(AppConstants.M_FOWLER, "Martin Fowler", "mFowler@email.com");
commands.bookAddedToAuthor("Domain-Driven Design", 60.08, AppConstants.E_EVANS);
commands.bookAddedToAuthor("Effective Java", 40.54, AppConstants.J_BLOCH);
commands.bookAddedToAuthor("Java Puzzlers", 39.99, AppConstants.J_BLOCH);
commands.bookAddedToAuthor("Java Concurrency in Practice", 29.40, AppConstants.J_BLOCH);
commands.bookAddedToAuthor("Patterns of Enterprise"
+ " Application Architecture", 54.01, AppConstants.M_FOWLER);
commands.bookAddedToAuthor("Domain Specific Languages", 48.89, AppConstants.M_FOWLER);
commands.authorNameUpdated(AppConstants.E_EVANS, "Eric J. Evans");
// Query the database using QueryService
var queries = new QueryServiceImpl();
var nullAuthor = queries.getAuthorByUsername("username");
var evans = queries.getAuthorByUsername(AppConstants.E_EVANS);
var blochBooksCount = queries.getAuthorBooksCount(AppConstants.J_BLOCH);
var authorsCount = queries.getAuthorsCount();
var dddBook = queries.getBook("Domain-Driven Design");
var blochBooks = queries.getAuthorBooks(AppConstants.J_BLOCH);
LOGGER.info("Author username : {}", nullAuthor);
LOGGER.info("Author evans : {}", evans);
LOGGER.info("jBloch number of books : {}", blochBooksCount);
LOGGER.info("Number of authors : {}", authorsCount);
LOGGER.info("DDD book : {}", dddBook);
LOGGER.info("jBloch books : {}", blochBooks);
HibernateUtil.getSessionFactory().close();
}
```
2. Query Service: The `QueryServiceImpl` class is used for read operations. It provides methods to get author and book details. Here's a snippet of how it's used:
1. Command Service: The `CommandServiceImpl` class is used for write operations. It provides methods to create authors and books, and to add books to authors.
```java
var queries = new QueryServiceImpl();
var evans = queries.getAuthorByUsername(AppConstants.E_EVANS);
var dddBook = queries.getBook("Domain-Driven Design");
```
2. Query Service: The `QueryServiceImpl` class is used for read operations. It provides methods to get author and book details.
This separation of concerns allows for flexibility in how the application handles data access and manipulation, and is a key aspect of the CQRS pattern.
## Class diagram
Program output:
![CQRS](./etc/cqrs.png "CQRS")
```
17:37:56.040 [main] INFO com.iluwatar.cqrs.app.App - Author username : null
17:37:56.040 [main] INFO com.iluwatar.cqrs.app.App - Author evans : Author(name=Eric J. Evans, email=evans@email.com, username=eEvans)
17:37:56.041 [main] INFO com.iluwatar.cqrs.app.App - jBloch number of books : 3
17:37:56.041 [main] INFO com.iluwatar.cqrs.app.App - Number of authors : 3
17:37:56.041 [main] INFO com.iluwatar.cqrs.app.App - DDD book : Book(title=Domain-Driven Design, price=60.08)
17:37:56.042 [main] INFO com.iluwatar.cqrs.app.App - jBloch books : [Book(title=Effective Java, price=40.54), Book(title=Java Puzzlers, price=39.99), Book(title=Java Concurrency in Practice, price=29.4)]
```
## Applicability
@@ -50,9 +50,10 @@ public class App {
* @param args command line args
*/
public static void main(String[] args) {
var commands = new CommandServiceImpl();
// Create Authors and Books using CommandService
var commands = new CommandServiceImpl();
commands.authorCreated(AppConstants.E_EVANS, "Eric Evans", "evans@email.com");
commands.authorCreated(AppConstants.J_BLOCH, "Joshua Bloch", "jBloch@email.com");
commands.authorCreated(AppConstants.M_FOWLER, "Martin Fowler", "mFowler@email.com");
@@ -66,9 +67,9 @@ public class App {
commands.bookAddedToAuthor("Domain Specific Languages", 48.89, AppConstants.M_FOWLER);
commands.authorNameUpdated(AppConstants.E_EVANS, "Eric J. Evans");
// Query the database using QueryService
var queries = new QueryServiceImpl();
// Query the database using QueryService
var nullAuthor = queries.getAuthorByUsername("username");
var evans = queries.getAuthorByUsername(AppConstants.E_EVANS);
var blochBooksCount = queries.getAuthorBooksCount(AppConstants.J_BLOCH);
+4 -15
View File
@@ -77,7 +77,7 @@ public class Wizard {
}
```
Next, we have the goblin who's the target of the spells.
Next, we have the `Goblin` who's the `Target` of the spells.
```java
@Slf4j
@@ -89,31 +89,24 @@ public abstract class Target {
private Visibility visibility;
/**
* Print status.
*/
public void printStatus() {
LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
}
/**
* Changes the size of the target.
*/
public void changeSize() {
var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
setSize(oldSize);
}
/**
* Changes the visibility of the target.
*/
public void changeVisibility() {
var visible = getVisibility() == Visibility.INVISIBLE
? Visibility.VISIBLE : Visibility.INVISIBLE;
setVisibility(visible);
}
}
```
```java
public class Goblin extends Target {
public Goblin() {
@@ -128,7 +121,7 @@ public class Goblin extends Target {
}
```
Finally, we have the wizard in the main function casting spells.
Finally, we can show the full example of `Wizard` casting spells.
```java
public static void main(String[] args) {
@@ -169,10 +162,6 @@ Here's the program output:
20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=invisible]
```
## Class diagram
![Command](./etc/command.png "Command")
## Applicability
Use the Command pattern when you want to:
+91 -43
View File
@@ -29,64 +29,112 @@ In plain words
**Programmatic Example**
Managing transactions across different services in a distributed system, such as an e-commerce platform with separate Payment and Shipping microservices, requires careful coordination to avoid issues. When a user places an order but one service (e.g., Payment) is unavailable while the other (e.g., Shipping) is ready, we need a robust solution to handle this discrepancy.
Managing transactions across different services in a distributed system, such as an e-commerce platform with separate `Payment` and `Shipping` microservices, requires careful coordination to avoid issues. When a user places an order but one service (e.g., `Payment`) is unavailable while the other (e.g., `Shipping`) is ready, we need a robust solution to handle this discrepancy.
A strategy to address this involves using a Commander component that orchestrates the process. Initially, the order is processed by the available service (Shipping in this case). The commander then attempts to synchronize the order with the currently unavailable service (Payment) by storing the order details in a database or queueing it for future processing. This queueing system must also account for possible failures in adding requests to the queue.
A strategy to address this involves using a `Commander` component that orchestrates the process. Initially, the order is processed by the available service (`Shipping` in this case). The commander then attempts to synchronize the order with the currently unavailable service (`Payment`) by storing the order details in a database or queueing it for future processing. This queueing system must also account for possible failures in adding requests to the queue.
The commander repeatedly tries to process the queued orders to ensure both services eventually reflect the same transaction data. This process involves ensuring idempotence, meaning that even if the same order synchronization request is made multiple times, it will only be executed once, preventing duplicate transactions. The goal is to achieve eventual consistency across services, where all systems are synchronized over time despite initial failures or delays.
The `Commander` repeatedly tries to process the queued orders to ensure both services eventually reflect the same transaction data. This process involves ensuring idempotence, meaning that even if the same order synchronization request is made multiple times, it will only be executed once, preventing duplicate transactions. The goal is to achieve eventual consistency across services, where all systems are synchronized over time despite initial failures or delays.
In the provided code, the Commander pattern is used to handle distributed transactions across multiple services (PaymentService, ShippingService, MessagingService, EmployeeHandle). Each service has its own database and can throw exceptions to simulate failures.
The Commander class is the central part of this pattern. It takes instances of all services and their databases, along with some configuration parameters. The placeOrder method in the Commander class is used to place an order, which involves interacting with all the services.
To get a grasp of how this works in practice, let's see `AppShippingFailCases` class and explain afterward how it works.
```java
public class Commander {
// ... constructor and other methods ...
public class AppShippingFailCases {
public void placeOrder(Order order) {
// ... implementation ...
private static final RetryParams retryParams = RetryParams.DEFAULT;
private static final TimeLimits timeLimits = TimeLimits.DEFAULT;
void itemUnavailableCase() {
var ps = new PaymentService(new PaymentDatabase());
var ss = new ShippingService(new ShippingDatabase(), new ItemUnavailableException());
var ms = new MessagingService(new MessagingDatabase());
var eh = new EmployeeHandle(new EmployeeDatabase());
var qdb = new QueueDatabase();
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
var user = new User("Jim", "ABCD");
var order = new Order(user, "book", 10f);
c.placeOrder(order);
}
void shippingNotPossibleCase() {
var ps = new PaymentService(new PaymentDatabase());
var ss = new ShippingService(new ShippingDatabase(), new ShippingNotPossibleException());
var ms = new MessagingService(new MessagingDatabase());
var eh = new EmployeeHandle(new EmployeeDatabase());
var qdb = new QueueDatabase();
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
var user = new User("Jim", "ABCD");
var order = new Order(user, "book", 10f);
c.placeOrder(order);
}
void shippingDatabaseUnavailableCase() {
//rest is successful
var ps = new PaymentService(new PaymentDatabase());
var ss = new ShippingService(new ShippingDatabase(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException());
var ms = new MessagingService(new MessagingDatabase());
var eh = new EmployeeHandle(new EmployeeDatabase());
var qdb = new QueueDatabase();
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
var user = new User("Jim", "ABCD");
var order = new Order(user, "book", 10f);
c.placeOrder(order);
}
void shippingSuccessCase() {
//goes to payment after 2 retries maybe - rest is successful for now
var ps = new PaymentService(new PaymentDatabase(), new DatabaseUnavailableException());
var ss = new ShippingService(new ShippingDatabase(), new DatabaseUnavailableException(),
new DatabaseUnavailableException());
var ms = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException());
var eh = new EmployeeHandle(new EmployeeDatabase());
var qdb = new QueueDatabase();
var c = new Commander(eh, ps, ss, ms, qdb, retryParams, timeLimits);
var user = new User("Jim", "ABCD");
var order = new Order(user, "book", 10f);
c.placeOrder(order);
}
public static void main(String[] args) {
var asfc = new AppShippingFailCases();
asfc.shippingSuccessCase();
}
}
```
The User and Order classes represent a user and an order respectively. An order is placed by a user.
The `AppShippingFailCases` class is designed to simulate different scenarios where the Shipping service is available or unavailable. It uses the Commander pattern to handle distributed transactions across multiple services.
```java
public class User {
// ... constructor and other methods ...
}
Here's a breakdown of the methods in the `AppShippingFailCases` class:
1. `itemUnavailableCase`: This method simulates a scenario where the item to be shipped is unavailable. It creates instances of the `Commander` class with the `ShippingService` throwing an `ItemUnavailableException`. An order is placed and the system tries to handle this failure.
2. `shippingNotPossibleCase`: This method simulates a scenario where shipping is not possible. It creates instances of the `Commander` class with the `ShippingService` throwing a `ShippingNotPossibleException`. An order is placed and the system tries to handle this failure.
3. `shippingDatabaseUnavailableCase`: This method simulates a scenario where the `ShippingService` and `ShippingDatabase` are unavailable. It creates instances of the `Commander` class with the `ShippingService` throwing multiple `DatabaseUnavailableException`. An order is placed and the system tries to handle this failure.
4. `shippingSuccessCase`: This method simulates a successful scenario where all services are available except for some temporary unavailability of the `PaymentService`, `ShippingService`, and `MessagingService`. An order is placed and the system handles this situation.
In each of these methods, a `Commander` instance is created with the respective services and their databases. Then, a `User` and an `Order` are created, and the `placeOrder` method of the `Commander` instance is called with the order. This triggers the process of placing the order and handling any failures according to the Commander pattern.
In the `main` method, the `shippingSuccessCase` method is called to simulate a successful scenario.
Finally, let's execute the `main` method see the program output.
public class Order {
// ... constructor and other methods ...
}
```
Each service (e.g., PaymentService, ShippingService, MessagingService, EmployeeHandle) has its own database and can throw exceptions to simulate failures. For example, the PaymentService might throw a DatabaseUnavailableException if its database is unavailable.
```java
public class PaymentService {
// ... constructor and other methods ...
}
18:01:07.738 [main] DEBUG com.iluwatar.commander.Commander -- Order I07V78ZOB8RZ: Error in connecting to shipping service, trying again..
18:01:10.536 [main] DEBUG com.iluwatar.commander.Commander -- Order I07V78ZOB8RZ: Error in connecting to shipping service, trying again..
18:01:15.401 [main] INFO com.iluwatar.commander.Commander -- Order I07V78ZOB8RZ: Shipping placed successfully, transaction id: RCM0PO9N9B6J
18:01:15.401 [main] INFO com.iluwatar.commander.Commander -- Order has been placed and will be shipped to you. Please wait while we make your payment...
18:01:15.407 [Thread-0] DEBUG com.iluwatar.commander.Commander -- Order I07V78ZOB8RZ: Error in connecting to payment service, trying again..
18:01:18.327 [Thread-0] INFO com.iluwatar.commander.Commander -- Order I07V78ZOB8RZ: Payment successful, transaction Id: UWS72C00JN9Q
18:01:18.328 [Thread-0] INFO com.iluwatar.commander.Commander -- Payment made successfully, thank you for shopping with us!!
18:01:18.332 [Thread-1] DEBUG com.iluwatar.commander.Commander -- Order I07V78ZOB8RZ: Error in connecting to messaging service (Payment Success msg), trying again..
18:01:20.693 [Thread-1] INFO com.iluwatar.commander.messagingservice.MessagingService -- Msg: Your order has been placed and paid for successfully! Thank you for shopping with us!
18:01:20.694 [Thread-1] INFO com.iluwatar.commander.Commander -- Order I07V78ZOB8RZ: Payment Success message sent, request Id: 72DOWH1D0WYS
```
The DatabaseUnavailableException, ItemUnavailableException, and ShippingNotPossibleException classes represent different types of exceptions that can occur.
```java
public class DatabaseUnavailableException extends Exception {
// ... constructor and other methods ...
}
public class ItemUnavailableException extends Exception {
// ... constructor and other methods ...
}
public class ShippingNotPossibleException extends Exception {
// ... constructor and other methods ...
}
```
In the main method of each class (AppQueueFailCases, AppShippingFailCases), different scenarios are simulated by creating instances of the Commander class with different configurations and calling the placeOrder method.
## Applicability
Use the Commander pattern for distributed transactions when:
@@ -134,4 +134,4 @@ public class AppQueueFailCases {
var aqfc = new AppQueueFailCases();
aqfc.queueSuccessCase();
}
}
}
@@ -43,8 +43,8 @@ import com.iluwatar.commander.shippingservice.ShippingService;
*/
public class AppShippingFailCases {
private static final RetryParams retryParams = RetryParams.DEFAULT;
private static final RetryParams retryParams = RetryParams.DEFAULT;
private static final TimeLimits timeLimits = TimeLimits.DEFAULT;
void itemUnavailableCase() {
@@ -111,4 +111,4 @@ public class AppShippingFailCases {
var asfc = new AppShippingFailCases();
asfc.shippingSuccessCase();
}
}
}
-10
View File
@@ -102,12 +102,6 @@ Upon opening the component package, the collection of components are revealed. T
public class PlayerInputComponent implements InputComponent {
private static final int walkAcceleration = 1;
/**
* The update method to change the velocity based on the input key event.
*
* @param gameObject the gameObject instance
* @param e key event instance
*/
@Override
public void update(GameObject gameObject, int e) {
switch (e) {
@@ -128,10 +122,6 @@ public class PlayerInputComponent implements InputComponent {
}
```
## Class diagram
![Component](./etc/component.uml.png "Component")
## Applicability
* Used in game development and simulations where game entities (e.g., characters, items) can have a dynamic set of abilities or states.
+1 -5
View File
@@ -22,7 +22,7 @@ The Composite Entity design pattern is aimed at managing a set of interrelated p
## Explanation
Real world example
Real-world example
> Consider a university registration system where a "Student" entity is a composite entity. Each "Student" object includes several dependent objects: personal details, course enrollments, grades, and payment information. Instead of managing each of these aspects separately, the Composite Entity pattern allows the university system to treat the "Student" as a single entity. This simplifies operations such as enrolling a student in a new course, updating grades, and processing payments, since all related actions can be managed through the composite "Student" object.
@@ -123,10 +123,6 @@ public App(String message, String signal) {
}
```
## Class diagram
![Composite Entity](./etc/composite_entity.urm.png "Composite Entity")
## Applicability
* Useful in enterprise applications where business objects are complex and involve various interdependent objects.
-4
View File
@@ -174,10 +174,6 @@ The console output:
Much wind pours from your mouth.
```
## Class diagram
![Composite](./etc/composite.urm.png "Composite class diagram")
## Applicability
Use the Composite pattern when
+6 -6
View File
@@ -38,7 +38,7 @@ In plain words
This application has different layers labelled A, B and C with each extracting specific information from a similar context for further use in the software. Passing down each pieces of information individually would be inefficient, a method to efficiently store and pass information is needed.
Define the data that the service context object contains.
Define the data that the `ServiceContext` object contains.
```Java
@Getter
@@ -51,7 +51,7 @@ public class ServiceContext {
}
```
Create an interface used in parts of the application for context objects to be created.
Create interface `ServiceContextFactory` to be used in parts of the application for context objects to be created.
```Java
public class ServiceContextFactory {
@@ -78,7 +78,9 @@ public class LayerA {
context.setACCOUNT_SERVICE(accountService);
}
}
```
```Java
@Getter
public class LayerB {
@@ -92,7 +94,9 @@ public class LayerB {
context.setSESSION_SERVICE(sessionService);
}
}
```
```Java
@Getter
public class LayerC {
@@ -150,10 +154,6 @@ Program output:
08:15:32.137 [main] INFO com.iluwatar.context.object.App -- Context = com.iluwatar.context.object.ServiceContext@5577140b
```
## Class diagram
![Context Object](./etc/context-object.png "Context object")
## Applicability
* When there is a need to abstract and encapsulate context information from different parts of an application to avoid cluttering the business logic with environment-specific code.
+2 -2
View File
@@ -22,7 +22,7 @@ The purpose of the Converter pattern is to provide a generic, common way of bidi
## Explanation
Real world example
Real-world example
> In a real-world scenario, consider a scenario where a library system needs to interact with a third-party book database. The library system uses its own internal book format, while the third-party database provides book information in a different format. To facilitate communication between the two systems, a Converter design pattern can be employed. This pattern will define a converter class that transforms the third-party book data format into the library's internal book format and vice versa. This ensures that the library system can seamlessly integrate with the third-party database without altering its own internal structure or the third-party system's format.
@@ -34,7 +34,7 @@ In plain words
In real world applications it is often the case that database layer consists of entities that need to be mapped into DTOs for use on the business logic layer. Similar mapping is done for potentially huge amount of classes, and we need a generic way to achieve this.
We need a generic solution for the mapping problem. To achieve this, let's introduce a generic converter.
We need a generic solution for the mapping problem. To achieve this, let's introduce a generic `Converter`.
```java
public class Converter<T, U> {
+2 -24
View File
@@ -51,7 +51,9 @@ public class Book {
this.publicationDate = publicationDate;
}
}
```
```java
public enum Genre {
FANTASY,
HORROR,
@@ -70,9 +72,6 @@ Book createBook(Genre genre, String author, String title, LocalDate publicationD
However, what if we only wanted to create books from the `FANTASY` genre? Passing the `FANTASY` parameter with each method call would be repetitive. Alternatively, we could define a new method specifically for creating `FANTASY` books, but it would be impractical to create a separate method for each genre. The solution is to use a curried function.
```java
/**
* Curried book builder/creator function.
*/
static Function<Genre, Function<String, Function<String, Function<LocalDate, Book>>>> book_creator
= bookGenre
-> bookAuthor
@@ -90,11 +89,6 @@ Function<String, Function<String, Function<LocalDate, Book>>> fantasyBookFunc =
Unfortunately, the type signature of `BOOK_CREATOR` and `fantasyBookFunc` are difficult to read and understand. We can improve this by using the [builder pattern](https://java-design-patterns.com/patterns/builder/) and functional interfaces.
```java
/**
* Implements the builder pattern using functional interfaces to create a more readable book
* creator function. This function is equivalent to the BOOK_CREATOR function.
*/
public static AddGenre builder() {
return genre
-> author
@@ -103,30 +97,18 @@ public static AddGenre builder() {
-> new Book(genre, author, title, publicationDate);
}
/**
* Functional interface which adds the genre to a book.
*/
public interface AddGenre {
Book.AddAuthor withGenre(Genre genre);
}
/**
* Functional interface which adds the author to a book.
*/
public interface AddAuthor {
Book.AddTitle withAuthor(String author);
}
/**
* Functional interface which adds the title to a book.
*/
public interface AddTitle {
Book.AddPublicationDate withTitle(String title);
}
/**
* Functional interface which adds the publication date to a book.
*/
public interface AddPublicationDate {
Book withPublicationDate(LocalDate publicationDate);
}
@@ -202,10 +184,6 @@ Program output:
09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=SCIFI, author='Isaac Asimov', title='Foundation', publicationDate=1942-05-01}
```
## Class diagram
![Currying](./etc/currying.urm.png)
## Applicability
* When functions need to be called with some arguments preset.
+1 -1
View File
@@ -22,7 +22,7 @@ The Data Access Object (DAO) design pattern aims to separate the application's b
## Explanation
Real world example
Real-world example
> Imagine a library system where the main application manages book loans, user accounts, and inventory. The Data Access Object (DAO) pattern in this context would be used to separate the database operations (such as fetching book details, updating user records, and checking inventory) from the business logic of managing loans and accounts. For instance, there would be a `BookDAO` class responsible for all database interactions related to books, such as retrieving a book by its ISBN or updating its availability status. This abstraction allows the library system's main application code to focus on business rules and workflows, while the `BookDAO` handles the complex SQL queries and data management. This separation makes the system easier to maintain and test, as changes to the data source or business logic can be managed independently.
-19
View File
@@ -55,29 +55,14 @@ public class DataBus {
return INSTANCE;
}
/**
* Register a member with the data-bus to start receiving events.
*
* @param member The member to register
*/
public void subscribe(final Member member) {
this.listeners.add(member);
}
/**
* Deregister a member to stop receiving events.
*
* @param member The member to deregister
*/
public void unsubscribe(final Member member) {
this.listeners.remove(member);
}
/**
* Publish an event to all members.
*
* @param event The event
*/
public void publish(final DataType event) {
event.setDataBus(this);
@@ -163,10 +148,6 @@ When the data bus publishes a message, the output is as follows:
As shown, `MessageCollectorMembers` only accept messages of type `MessageData`, so they do not see the `StartingData` or `StoppingData` messages, which are only visible to `StatusMember` (the event administrators or organizers). This selective message handling prevents ordinary community members from receiving administrative notifications.
## Class diagram
![Data Bus](./etc/data-bus.urm.png "Data Bus pattern")
## Applicability
* When multiple components need to share data or events but direct coupling is undesirable.
+4 -4
View File
@@ -35,7 +35,7 @@ The Data Locality pattern is a design pattern that aims to improve performance b
In the data-locality module, the pattern is demonstrated using a game loop that processes a bunch of game entities. These entities are decomposed into different domains: AI, physics, and rendering.
The GameEntity class is the main class that represents a game entity. It contains an array of AiComponent, PhysicsComponent, and RenderComponent objects. These components represent different aspects of a game entity.
The `GameEntity` class is the main class that represents a game entity. It contains an array of `AiComponent`, `PhysicsComponent`, and `RenderComponent` objects. These components represent different aspects of a game entity.
```java
public class GameEntity {
@@ -46,7 +46,7 @@ public class GameEntity {
}
```
The GameEntity class has a start method that initializes all the components.
The `GameEntity` class has a start method that initializes all the components.
```java
public void start() {
@@ -58,7 +58,7 @@ public void start() {
}
```
The GameEntity class also has an update method that updates all the components. This method demonstrates the data locality pattern. Instead of updating all aspects of a single entity at a time (AI, physics, and rendering), it updates the same aspect (e.g., AI) for all entities first, then moves on to the next aspect (e.g., physics). This approach improves cache utilization because it's more likely that the data needed for the update is already in the cache.
The `GameEntity` class also has an update method that updates all the components. This method demonstrates the data locality pattern. Instead of updating all aspects of a single entity at a time (AI, physics, and rendering), it updates the same aspect (e.g., AI) for all entities first, then moves on to the next aspect (e.g., physics). This approach improves cache utilization because it's more likely that the data needed for the update is already in the cache.
```java
public void update() {
@@ -74,7 +74,7 @@ public void update() {
}
```
The Application class contains the main method that creates a GameEntity object and starts the game loop.
The `Application` class contains the main method that creates a `GameEntity` object and starts the game loop.
```java
public class Application {
+5 -9
View File
@@ -36,9 +36,9 @@ Wikipedia says
The Data Mapper is a design pattern that separates the in-memory objects from the database. Its responsibility is to transfer data between the two and also to isolate them from each other. This pattern promotes the [Single Responsibility Principle](https://java-design-patterns.com/principles/#single-responsibility-principle) and [Separation of Concerns](https://java-design-patterns.com/principles/#separation-of-concerns).
In the data-mapper module, the pattern is demonstrated using a Student class and a StudentDataMapper interface.
In the data-mapper module, the pattern is demonstrated using a `Student` class and a `StudentDataMapper` interface.
The Student class is a simple POJO (Plain Old Java Object) that represents a student. It has properties like studentId, name, and grade.
The `Student` class is a simple POJO (Plain Old Java Object) that represents a student. It has properties like studentId, name, and grade.
```java
public class Student {
@@ -49,7 +49,7 @@ public class Student {
}
```
The StudentDataMapper interface defines the operations that can be performed on Student objects. These operations include insert, update, delete, and find.
The `StudentDataMapper` interface defines the operations that can be performed on `Student` objects. These operations include `insert`, `update`, `delete`, and `find`.
```java
public interface StudentDataMapper {
@@ -64,7 +64,7 @@ public interface StudentDataMapper {
}
```
The StudentDataMapperImpl class implements the StudentDataMapper interface. It contains the actual logic for interacting with the database.
The `StudentDataMapperImpl` class implements the `StudentDataMapper` interface. It contains the actual logic for interacting with the database.
```java
public class StudentDataMapperImpl implements StudentDataMapper {
@@ -92,7 +92,7 @@ public class StudentDataMapperImpl implements StudentDataMapper {
}
```
The App class contains the main method that demonstrates the use of the StudentDataMapper. It creates a Student object, inserts it into the database, finds it, updates it, and finally deletes it.
The `App` class contains the main method that demonstrates the use of the `StudentDataMapper`. It creates a `Student` object, inserts it into the database, finds it, updates it, and finally deletes it.
```java
public class App {
@@ -117,10 +117,6 @@ Program output:
13:54:29.238 [main] DEBUG com.iluwatar.datamapper.App -- App.main(), student : Student(studentId=1, name=AdamUpdated, grade=A), is going to be deleted
```
## Class diagram
![Data Mapper](./etc/data-mapper.png "Data Mapper")
## Applicability
Use the Data Mapper in any of the following situations
-4
View File
@@ -131,10 +131,6 @@ Program output:
11:34:18.101 [main] INFO com.iluwatar.decorator.App -- Clubbed troll power: 20.
```
## Class diagram
![Decorator](./etc/decorator.urm.png "Decorator pattern class diagram")
## Applicability
Decorator is used to:
+2 -43
View File
@@ -20,7 +20,7 @@ The Double Buffer pattern aims to reduce the time necessary for rendering and di
## Explanation
Real world example
Real-world example
> Imagine a busy restaurant kitchen where chefs are constantly preparing dishes, and waitstaff are constantly picking up ready dishes to serve to customers. To avoid confusion and delays, the restaurant uses a double buffer system. They have two counters: one for chefs to place newly prepared dishes and another for waitstaff to pick up the dishes. While the chefs are filling one counter with prepared dishes, the waitstaff are simultaneously clearing the other counter by picking up dishes to serve. Once the waitstaff have cleared all dishes from their counter, they switch to the counter where the chefs have placed the newly prepared dishes, and the chefs start filling the now-empty counter. This system ensures a smooth and continuous workflow without either party waiting idly, maximizing efficiency and minimizing downtime.
@@ -39,48 +39,21 @@ A typical example, and one that every game engine must address, is rendering. Wh
`Buffer` interface that assures basic functionalities of a buffer.
```java
/**
* Buffer interface.
*/
public interface Buffer {
/**
* Clear the pixel in (x, y).
*
* @param x X coordinate
* @param y Y coordinate
*/
void clear(int x, int y);
/**
* Draw the pixel in (x, y).
*
* @param x X coordinate
* @param y Y coordinate
*/
void draw(int x, int y);
/**
* Clear all the pixels.
*/
void clearAll();
/**
* Get all the pixels.
*
* @return pixel list
*/
Pixel[] getPixels();
}
```
One of the implementations of `Buffer` interface.
```java
/**
* FrameBuffer implementation class.
*/
public class FrameBuffer implements Buffer {
public static final int WIDTH = 10;
@@ -121,22 +94,16 @@ public class FrameBuffer implements Buffer {
We support black and white pixels.
```java
/**
* Pixel enum. Each pixel can be white (not drawn) or black (drawn).
*/
public enum Pixel {
WHITE,
BLACK;
BLACK
}
```
`Scene` represents the game scene where current buffer has already been rendered.
```java
/**
* Scene class. Render the output frame.
*/
@Slf4j
public class Scene {
@@ -146,9 +113,6 @@ public class Scene {
private int next;
/**
* Constructor of Scene.
*/
public Scene() {
frameBuffers = new FrameBuffer[2];
frameBuffers[0] = new FrameBuffer();
@@ -157,11 +121,6 @@ public class Scene {
next = 1;
}
/**
* Draw the next frame.
*
* @param coordinateList list of pixels of which the color should be black
*/
public void draw(List<? extends Pair<Integer, Integer>> coordinateList) {
LOGGER.info("Start drawing next frame");
LOGGER.info("Current buffer: " + current + " Next buffer: " + next);
+1 -1
View File
@@ -15,7 +15,7 @@ The Double-Checked Locking pattern aims to reduce the overhead of acquiring a lo
## Explanation
Real world example
Real-world example
> In a company with a high-value equipment room, employees first check a visible sign to see if the room is locked. If the sign shows it's unlocked, they enter directly; if locked, they use a security keycard for access. This two-step verification process efficiently manages security without unnecessary use of the electronic lock system, mirroring the Double-Checked Locking pattern used in software to minimize resource-intensive operations.
+2 -2
View File
@@ -19,7 +19,7 @@ The Double Dispatch pattern is used to achieve dynamic polymorphism based on the
## Explanation
Real world example
Real-world example
> In a logistics company, different types of delivery vehicles like trucks, drones, and bikes interact with various types of packages (fragile, oversized, standard). The Double Dispatch design pattern is used to determine the optimal delivery method: trucks might handle oversized items, drones for quick deliveries of light packages, and bikes for urban areas. Each vehicle-package combination results in a different handling and delivery strategy, dynamically determined at runtime based on the types of both the vehicle and the package.
@@ -143,7 +143,7 @@ Trade-offs:
* [Strategy](https://java-design-patterns.com/patterns/strategy/): Similar in intent where it's used to choose an algorithm at runtime, though Strategy focuses on single object context rather than interactions between multiple objects.
* [Visitor](https://java-design-patterns.com/patterns/visitor/): Often used together with Double Dispatch to encapsulate operations performed on a set of element objects.
## Real world examples
## Credits
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/4awj7cV)
* [Java Design Pattern Essentials](https://amzn.to/3Jg8ZZV)
+2
View File
@@ -40,7 +40,9 @@ In our programmatic example, we demonstrate the implementation of an event aggre
public interface EventObserver {
void onEvent(Event e);
}
```
```java
public abstract class EventEmitter {
private final Map<Event, List<EventObserver>> observerLists;
-4
View File
@@ -141,10 +141,6 @@ In this snippet, when a `SyncEvent` is started, it runs on the main thread, bloc
These are the key parts of the Event-Based Asynchronous design pattern as implemented in this code. The pattern allows tasks to be executed in the background, notifying the main program via events when completed, thereby enhancing system efficiency and responsiveness without blocking ongoing operations.
## Class diagram
![Event-Based Asynchronous](./etc/event-asynchronous.png "Event-Based Asynchronous")
## Applicability
* When multiple tasks can be processed in parallel and independently.
+6
View File
@@ -51,7 +51,9 @@ First, we'll define the `Event` abstract class and the concrete event classes `U
public abstract class Event {
// Event related properties and methods
}
```
```java
public class UserCreatedEvent extends Event {
private User user;
@@ -63,7 +65,9 @@ public class UserCreatedEvent extends Event {
return user;
}
}
```
```java
public class UserUpdatedEvent extends Event {
private User user;
@@ -85,7 +89,9 @@ public class UserCreatedEventHandler {
// Logic to execute when a UserCreatedEvent occurs
}
}
```
```java
public class UserUpdatedEventHandler {
public void onUserUpdated(UserUpdatedEvent event) {
// Logic to execute when a UserUpdatedEvent occurs
-4
View File
@@ -194,10 +194,6 @@ Running the example produces the following console output.
In this example, the state of the system can be recreated at any point by replaying the events in the queue. This is a key feature of the Event Sourcing pattern.
## Class diagram
![Event Sourcing](./etc/event-sourcing.png "Event Sourcing")
## Applicability
* In systems where complete audit trails and historical changes are crucial.
-4
View File
@@ -195,10 +195,6 @@ Program output:
06:07:20.678 [main] INFO com.iluwatar.facade.DwarvenMineWorker -- Dwarven tunnel digger goes to sleep.
```
## Class diagram
![Facade](./etc/facade.urm.png "Facade pattern class diagram")
## Applicability
Use the Facade pattern when
-4
View File
@@ -85,10 +85,6 @@ Program output:
06:40:07.272 [main] INFO com.iluwatar.factory.method.App -- The elf blacksmith manufactured an elven axe
```
## Class diagram
![Factory Method](./etc/factory-method.urm.png "Factory Method pattern class diagram")
## Applicability
Use the Factory Method pattern when:
+4
View File
@@ -34,7 +34,9 @@ We have an interface `Coin` and two implementations `GoldCoin` and `CopperCoin`.
public interface Coin {
String getDescription();
}
```
```java
public class GoldCoin implements Coin {
static final String DESCRIPTION = "This is a gold coin.";
@@ -44,7 +46,9 @@ public class GoldCoin implements Coin {
return DESCRIPTION;
}
}
```
```java
public class CopperCoin implements Coin {
static final String DESCRIPTION = "This is a copper coin.";
-4
View File
@@ -130,10 +130,6 @@ Running the example produces the following console output.
06:52:11.465 [main] INFO com.iluwatar.fanout.fanin.App -- Sum of all squared numbers --> 139
```
## Class diagram
![Fan-Out/Fan-In](./etc/fanout-fanin.png)
## Applicability
Appropriate in scenarios where tasks can be broken down and executed in parallel, especially suitable for data processing, batch processing, and situations requiring aggregation of results from various sources.
+2 -4
View File
@@ -43,7 +43,9 @@ public interface Threat {
int id();
ThreatType type();
}
```
```java
public interface ThreatAwareSystem {
String systemId();
List<? extends Threat> threats();
@@ -222,10 +224,6 @@ Running the example produces the following console output.
08:33:23.581 [main] INFO com.iluwatar.filterer.App -- Filtered by probability = 0.99 : SimpleProbabilisticThreatAwareSystem(systemId=Sys-1, threats=[SimpleProbableThreat{probability=0.99} SimpleThreat(threatType=TROJAN, id=1, name=Trojan-ArcBomb)])
```
## Class diagram
![Filterer](./etc/filterer.png "Filterer")
## Applicability
* Use when you need to filter a collection of objects dynamically based on different criteria.
-4
View File
@@ -150,10 +150,6 @@ Program output:
08:50:08.270 [main] INFO com.iluwatar.fluentinterface.app.App -- Last amongst first two negatives: -22
```
## Class diagram
![Fluent Interface](./etc/fluentinterface.png "Fluent Interface")
## Applicability
Use the Fluent Interface pattern when
+7 -5
View File
@@ -32,13 +32,15 @@ Wikipedia says
Alchemist's shop has shelves full of magic potions. Many of the potions are the same so there is no need to create a new object for each of them. Instead, one object instance can represent multiple shelf items so the memory footprint remains small.
First of all, we have different potion types:
First of all, we have different `Potion` types:
```java
public interface Potion {
void drink();
}
```
```java
@Slf4j
public class HealingPotion implements Potion {
@Override
@@ -46,7 +48,9 @@ public class HealingPotion implements Potion {
LOGGER.info("You feel healed. (Potion={})", System.identityHashCode(this));
}
}
```
```java
@Slf4j
public class HolyWaterPotion implements Potion {
@Override
@@ -54,7 +58,9 @@ public class HolyWaterPotion implements Potion {
LOGGER.info("You feel blessed. (Potion={})", System.identityHashCode(this));
}
}
```
```java
@Slf4j
public class InvisibilityPotion implements Potion {
@Override
@@ -170,10 +176,6 @@ Program output:
09:02:52.734 [main] INFO com.iluwatar.flyweight.HolyWaterPotion -- You feel blessed. (Potion=1689843956)
```
## Class diagram
![Flyweight](./etc/flyweight.urm.png "Flyweight pattern class diagram")
## Applicability
The Flyweight pattern's effectiveness depends heavily on how and where it's used. Apply the Flyweight pattern when all the following are true:
-4
View File
@@ -303,10 +303,6 @@ Current bullet position: 0.98999935
Stop variable-step game loop.
```
## Class diagram
![Game Loop](./etc/game-loop.urm.png "Game Loop pattern class diagram")
## Applicability
The Game Loop pattern is applicable in real-time simulation and gaming where the state needs to be updated continuously and consistently in response to user inputs and other events.
+4 -4
View File
@@ -50,14 +50,18 @@ public class ExternalServiceA implements Gateway {
// Implementation for ExternalServiceA
}
}
```
```java
public class ExternalServiceB implements Gateway {
@Override
public void execute() {
// Implementation for ExternalServiceB
}
}
```
```java
public class ExternalServiceC implements Gateway {
@Override
public void execute() {
@@ -122,10 +126,6 @@ Running the example produces the following output.
This example demonstrates how the Gateway design pattern can be used to simplify the interaction with multiple external services. Each service is encapsulated behind a common interface, and the application interacts with this interface rather than directly with the services. This reduces coupling and makes the application easier to maintain and extend.
## Class diagram
![Gateway](./etc/gateway.urm.png "Gateway")
## Applicability
Use the Gateway pattern when you need to integrate with remote services or APIs, and you want to minimize the coupling between your application and external systems. It is particularly useful in microservices architectures where different services need to communicate through well-defined APIs.
+4 -8
View File
@@ -31,8 +31,7 @@ Wikipedia says
**Programmatic Example**
* For the purpose of this demonstration assume we have already created a database instance **db**.
* Let's first look at the implementation of a person entity, and it's fields:
For the purpose of this demonstration assume we have already created a database instance **db**. Let's first look at the implementation of a person entity, and it's fields:
```java
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@@ -56,9 +55,7 @@ public final class Person implements Serializable {
```
* The following is the implementation of the personFinder which is the entity that the user will utilize in order
to search for a record in our database. It has the relevant DB attached to it. It also maintains an IdentityMap
to store the recently read records.
The following is the implementation of the `PersonFinder` which is the entity that the user will utilize in order to search for a record in our database. It has the relevant DB attached to it. It also maintains an `IdentityMap` to store the recently read records.
```java
@Slf4j
@@ -92,8 +89,7 @@ public class PersonFinder {
```
* The identity map field in the above class is simply an abstraction of a hashMap with **personNationalId**
as the keys and the corresponding person object as the value. Here is its implementation:
The identity map field in the above class is simply an abstraction of a hashMap with **personNationalId** as the keys and the corresponding person object as the value. Here is its implementation:
```java
@Slf4j
@@ -126,7 +122,7 @@ public class IdentityMap {
}
```
Now, let's see how the identity map works in our App's main function.
Now, let's see how the identity map works in our `App`'s `main` function.
```java
public static void main(String[] args) {
-4
View File
@@ -207,10 +207,6 @@ Program output:
13:36:37.090 [main] INFO com.iluwatar.iterator.App -- Next node: 14
```
## Class diagram
![Iterator](./etc/iterator_1.png "Iterator")
## Applicability
Use the Iterator pattern
-4
View File
@@ -136,10 +136,6 @@ public class App {
This is a basic example of the Leader/Followers pattern. The leader worker processes tasks and promotes a new leader once it finishes a task. The new leader then starts processing the next task, and the cycle continues.
## Class diagram
![Leader/Followers class diagram](./etc/leader-followers.png)
## Applicability
* Useful in scenarios where programs need to handle multiple services on a single thread to avoid resource thrashing and to improve scalability.
-4
View File
@@ -123,10 +123,6 @@ public class App {
In this example, the `LogProducer` services generate logs of different levels. The `LogAggregator` collects these logs and stores them in the `CentralLogStore` if they meet the minimum log level requirement. Finally, the logs are displayed by the `CentralLogStore`.
## Class diagram
![Log Aggregation](./etc/log-aggregation.png)
## Applicability
* Useful in distributed systems where logs from various components need to be centralized for better management and analysis.
-4
View File
@@ -182,10 +182,6 @@ public class SetVariableEvent implements UserEvent {
This example demonstrates the key aspects of the MVI pattern: unidirectional data flow, clear separation of concerns, and the use of events to drive changes in the Model's state.
## Class diagram
![Model-View-Intent](./etc/model-view-intent.png "Model-View-Intent")
## Applicability
* Useful in applications with complex user interfaces that require a clear separation of concerns and predictable state management.
-4
View File
@@ -133,10 +133,6 @@ public class App {
In this setup, the `App` class creates instances of the Model, View, and Presenter. It then connects these instances, forming the MVP triad. The Presenter is given a reference to the View, and the Model is set on the Presenter. Finally, the Presenter is started, which in turn opens the View.
## Class diagram
![Model-View-Presenter](./etc/model-view-presenter_1.png "Model-View-Presenter")
## Applicability
Use MVP in applications where a clear [separation of concerns](https://java-design-patterns.com/principles/#separation-of-concerns) is needed between the presentation layer and the underlying business logic. It's particularly useful in client-server applications and enterprise-level applications.
-4
View File
@@ -81,10 +81,6 @@ public class App {
In this way, the Mute Idiom allows us to simplify error handling by reducing boilerplate code for expected exceptions, enhancing code readability and maintainability, and allowing uninterrupted execution for non-critical exceptions.
## Class diagram
![Mute Idiom](./etc/mute-idiom.png "Mute Idiom")
## Applicability
* Useful in scenarios where certain exceptions are predictable and do not affect the overall logic or outcome.
-4
View File
@@ -161,10 +161,6 @@ Program output:
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenHobbits -- The hobbits are facing Sunny weather now
```
## Class diagram
![Observer](./etc/observer.png "Observer")
## Applicability
Use the Observer pattern in any of the following situations:
-4
View File
@@ -119,10 +119,6 @@ public class UserView {
In this example, the controllers (`SignupController` and `UserController`) are the Page Controllers. They handle the HTTP requests for their respective pages and determine which model and view to use. The models (`SignupModel` and `UserModel`) hold the data for the page, and the views (`SignupView` and `UserView`) determine how that data is presented. This separation of concerns makes the code easier to manage and maintain.
## Class diagram
![Page Controller](./etc/page-controller.urm.png)
## Applicability
* When developing a web application where each page or action needs specific processing.
+1 -1
View File
@@ -65,7 +65,7 @@ public class ParameterObject {
}
```
The `Builder` class inside `ParameterObject` provides a way to construct a `ParameterObject` instance. It has methods for setting each of the parameters, and a `build()` method to create the `ParameterObject`.
The `Builder` class inside `ParameterObject` provides a way to construct a `ParameterObject` instance. It has methods for setting each of the parameters, and a `build` method to create the `ParameterObject`.
```java
public static class Builder {
-4
View File
@@ -174,10 +174,6 @@ Running the application produces the following console output:
[pool-1-thread-2] ERROR ServiceExecutor - sleep interrupted
```
## Class diagram
![Queue-Based Load Leveling](./etc/queue-load-leveling.gif "Queue-Based Load Leveling")
## Applicability
* When there are variable workloads, and you need to ensure that peak loads do not overwhelm the system
-4
View File
@@ -140,10 +140,6 @@ Running the example outputs:
10:22:02.575 [main] INFO com.iluwatar.roleobject.ApplicationRoleObject -- Borrower Johny wants to get some money.
```
## Class diagram
![Role Object](./etc/role-object.urm.png "Role Object pattern class diagram")
## Applicability
* When an object needs to change its behavior dynamically based on its role.
-4
View File
@@ -158,10 +158,6 @@ Running the example produces the following console output:
This is a basic example of how to use the Saga design pattern. In a real-world application, the `Saga` class would manage the sequence of local transactions, ensuring that each transaction is performed in the correct order and that the Saga is rolled back if a transaction fails.
## Class diagram
![Saga](./etc/saga.urm.png "Saga pattern class diagram")
## Applicability
* When you have a complex transaction that spans multiple microservices.
+2 -2
View File
@@ -88,7 +88,7 @@ public class App {
}
```
The LoginHandler is responsible for handling login requests. When a user logs in, a session identifier is created and stored for future requests in a list.
The `LoginHandler` is responsible for handling login requests. When a user logs in, a session identifier is created and stored for future requests in a list.
```java
public class LoginHandler {
@@ -107,7 +107,7 @@ public class LoginHandler {
}
```
The LogoutHandler is responsible for handling logout requests. When a user logs out, the session identifier is deleted from the list along with the appropriate user session data.
The `LogoutHandler` is responsible for handling logout requests. When a user logs out, the session identifier is deleted from the list along with the appropriate user session data.
```java
public class LogoutHandler {
-4
View File
@@ -94,10 +94,6 @@ Console output:
This is a simple example of how the Service to Worker pattern can be implemented in a Java application.
## Class diagram
![Service to Worker](./etc/service-to-worker.png "Service to Worker")
## Applicability
* Use when you need to separate the controller logic from the view to improve code maintainability and enable team members to work on different parts of the application independently.
-4
View File
@@ -91,10 +91,6 @@ In this code, `SpatialPartition` is a class that represents the spatial partitio
This way, we can reduce the time complexity of finding the units within a certain range from O(n^2) to O(nlogn), decreasing the computations required significantly in case of a large number of units.
## Class diagram
![Spatial Partition](./etc/spatial-partition.urm.png "Spatial Partition pattern class diagram")
## Applicability
* Use when managing a large number of objects in a spatial environment, such as in games or simulations.
-4
View File
@@ -204,10 +204,6 @@ Console output:
12:49:24.818 [main] INFO com.iluwatar.specification.app.App -- Troll [size=large, movement=walking, color=dark, mass=4000.0kg]
```
## Class diagram
![Specification](./etc/specification.png "Specification")
## Applicability
* Use when you need to filter objects based on different criteria.