From 584e949714da5ef15e3fba872edfdbf66b98cee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 28 May 2024 19:46:39 +0300 Subject: [PATCH] docs: updates to several patterns --- abstract-document/README.md | 2 +- active-object/README.md | 7 +- acyclic-visitor/README.md | 2 +- adapter/README.md | 4 - aggregator-microservices/README.md | 6 +- ambassador/README.md | 6 +- anti-corruption-layer/README.md | 4 - api-gateway/README.md | 6 +- async-method-invocation/README.md | 4 - balking/README.md | 8 +- bridge/README.md | 4 +- builder/README.md | 10 +- bytecode/README.md | 2 +- caching/README.md | 6 +- callback/README.md | 6 +- chain-of-responsibility/README.md | 4 +- circuit-breaker/README.md | 70 ++++++++- client-session/README.md | 6 +- collecting-parameter/README.md | 140 ++++++------------ collection-pipeline/README.md | 2 +- .../README.md | 63 ++++++-- .../main/java/com/iluwatar/cqrs/app/App.java | 5 +- command/README.md | 19 +-- commander/README.md | 134 +++++++++++------ .../iluwatar/commander/AppQueueFailCases.java | 2 +- .../commander/AppShippingFailCases.java | 4 +- component/README.md | 10 -- composite-entity/README.md | 6 +- composite/README.md | 4 - context-object/README.md | 12 +- converter/README.md | 4 +- currying/README.md | 26 +--- data-access-object/README.md | 2 +- data-bus/README.md | 19 --- data-locality/README.md | 8 +- data-mapper/README.md | 14 +- decorator/README.md | 4 - double-buffer/README.md | 45 +----- double-checked-locking/README.md | 2 +- double-dispatch/README.md | 4 +- event-aggregator/README.md | 2 + event-based-asynchronous/README.md | 4 - event-driven-architecture/README.md | 6 + event-sourcing/README.md | 4 - facade/README.md | 4 - factory-method/README.md | 4 - factory/README.md | 4 + fanout-fanin/README.md | 4 - filterer/README.md | 6 +- fluent-interface/README.md | 4 - flyweight/README.md | 12 +- game-loop/README.md | 4 - gateway/README.md | 8 +- identity-map/README.md | 12 +- iterator/README.md | 4 - leader-followers/README.md | 4 - log-aggregation/README.md | 4 - model-view-intent/README.md | 4 - model-view-presenter/README.md | 4 - mute-idiom/README.md | 4 - observer/README.md | 4 - page-controller/README.md | 4 - parameter-object/README.md | 2 +- queue-based-load-leveling/README.md | 4 - role-object/README.md | 4 - saga/README.md | 4 - server-session/README.md | 4 +- service-to-worker/README.md | 4 - spatial-partition/README.md | 4 - specification/README.md | 4 - 70 files changed, 347 insertions(+), 479 deletions(-) diff --git a/abstract-document/README.md b/abstract-document/README.md index 358e0915e..b96e98dab 100644 --- a/abstract-document/README.md +++ b/abstract-document/README.md @@ -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. diff --git a/active-object/README.md b/active-object/README.md index 64dff1e5e..19a1fd690 100644 --- a/active-object/README.md +++ b/active-object/README.md @@ -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 { diff --git a/acyclic-visitor/README.md b/acyclic-visitor/README.md index 25127a599..40f656da3 100644 --- a/acyclic-visitor/README.md +++ b/acyclic-visitor/README.md @@ -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. diff --git a/adapter/README.md b/adapter/README.md index 9865528ee..8db42d7bd 100644 --- a/adapter/README.md +++ b/adapter/README.md @@ -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 diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md index a12fba3d9..652130ce1 100644 --- a/aggregator-microservices/README.md +++ b/aggregator-microservices/README.md @@ -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. diff --git a/ambassador/README.md b/ambassador/README.md index 551dfad1a..4b33cbf27 100644 --- a/ambassador/README.md +++ b/ambassador/README.md @@ -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. diff --git a/anti-corruption-layer/README.md b/anti-corruption-layer/README.md index 2a436a6ef..19eefd980 100644 --- a/anti-corruption-layer/README.md +++ b/anti-corruption-layer/README.md @@ -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: diff --git a/api-gateway/README.md b/api-gateway/README.md index 68771b8a2..fae8b7790 100644 --- a/api-gateway/README.md +++ b/api-gateway/README.md @@ -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. diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md index 6e6bb45e6..5c4d407a7 100644 --- a/async-method-invocation/README.md +++ b/async-method-invocation/README.md @@ -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 diff --git a/balking/README.md b/balking/README.md index 931397ca5..7eb2db7a2 100644 --- a/balking/README.md +++ b/balking/README.md @@ -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 diff --git a/bridge/README.md b/bridge/README.md index aa740c7bb..90d6b2e5b 100644 --- a/bridge/README.md +++ b/bridge/README.md @@ -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 { diff --git a/builder/README.md b/builder/README.md index ae4cf5e9c..bbeee408c 100644 --- a/builder/README.md +++ b/builder/README.md @@ -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 { diff --git a/bytecode/README.md b/bytecode/README.md index a31b6be40..3e53c2127 100644 --- a/bytecode/README.md +++ b/bytecode/README.md @@ -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. diff --git a/caching/README.md b/caching/README.md index 865878067..efc4c1f7e 100644 --- a/caching/README.md +++ b/caching/README.md @@ -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 diff --git a/callback/README.md b/callback/README.md index 1393b5f0d..b383ecebc 100644 --- a/callback/README.md +++ b/callback/README.md @@ -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 { diff --git a/chain-of-responsibility/README.md b/chain-of-responsibility/README.md index 39e383422..625d98d38 100644 --- a/chain-of-responsibility/README.md +++ b/chain-of-responsibility/README.md @@ -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 { diff --git a/circuit-breaker/README.md b/circuit-breaker/README.md index 595030780..f5e6b4251 100644 --- a/circuit-breaker/README.md +++ b/circuit-breaker/README.md @@ -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 diff --git a/client-session/README.md b/client-session/README.md index 3b619f461..2472fd32a 100644 --- a/client-session/README.md +++ b/client-session/README.md @@ -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 { diff --git a/collecting-parameter/README.md b/collecting-parameter/README.md index fb5f9c995..2f01d2caa 100644 --- a/collecting-parameter/README.md +++ b/collecting-parameter/README.md @@ -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(); - - // 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 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(); + + addValidA4Papers(result); + addValidA3Papers(result); + addValidA2Papers(result); + } + + public static void addValidA4Papers(Queue 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 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 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 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 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 diff --git a/collection-pipeline/README.md b/collection-pipeline/README.md index 033204c19..f60f06e01 100644 --- a/collection-pipeline/README.md +++ b/collection-pipeline/README.md @@ -49,7 +49,7 @@ public static List getModelsAfter2000(List 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> getGroupingOfCarsByCategory(List cars){ diff --git a/command-query-responsibility-segregation/README.md b/command-query-responsibility-segregation/README.md index 1437ff2ed..a25ddbb8f 100644 --- a/command-query-responsibility-segregation/README.md +++ b/command-query-responsibility-segregation/README.md @@ -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 diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java index 4552ede3d..52b8ead45 100644 --- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java +++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java @@ -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); diff --git a/command/README.md b/command/README.md index a8b202e37..e311f8816 100644 --- a/command/README.md +++ b/command/README.md @@ -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: diff --git a/commander/README.md b/commander/README.md index f05ad46f0..1e2d6fcbe 100644 --- a/commander/README.md +++ b/commander/README.md @@ -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: diff --git a/commander/src/main/java/com/iluwatar/commander/AppQueueFailCases.java b/commander/src/main/java/com/iluwatar/commander/AppQueueFailCases.java index b53793d82..5c5050272 100644 --- a/commander/src/main/java/com/iluwatar/commander/AppQueueFailCases.java +++ b/commander/src/main/java/com/iluwatar/commander/AppQueueFailCases.java @@ -134,4 +134,4 @@ public class AppQueueFailCases { var aqfc = new AppQueueFailCases(); aqfc.queueSuccessCase(); } -} \ No newline at end of file +} diff --git a/commander/src/main/java/com/iluwatar/commander/AppShippingFailCases.java b/commander/src/main/java/com/iluwatar/commander/AppShippingFailCases.java index efd924988..e15b1a911 100644 --- a/commander/src/main/java/com/iluwatar/commander/AppShippingFailCases.java +++ b/commander/src/main/java/com/iluwatar/commander/AppShippingFailCases.java @@ -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(); } -} \ No newline at end of file +} diff --git a/component/README.md b/component/README.md index e023b342c..4bc974603 100644 --- a/component/README.md +++ b/component/README.md @@ -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. diff --git a/composite-entity/README.md b/composite-entity/README.md index d90e88c91..015f44f90 100644 --- a/composite-entity/README.md +++ b/composite-entity/README.md @@ -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. diff --git a/composite/README.md b/composite/README.md index 0052ff8b9..8d4c5816d 100644 --- a/composite/README.md +++ b/composite/README.md @@ -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 diff --git a/context-object/README.md b/context-object/README.md index 94d80d567..5d31ffd98 100644 --- a/context-object/README.md +++ b/context-object/README.md @@ -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. diff --git a/converter/README.md b/converter/README.md index 75f455612..639c170c3 100644 --- a/converter/README.md +++ b/converter/README.md @@ -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 { diff --git a/currying/README.md b/currying/README.md index c804fca23..720937165 100644 --- a/currying/README.md +++ b/currying/README.md @@ -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>>> book_creator = bookGenre -> bookAuthor @@ -90,11 +89,6 @@ Function>> 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. diff --git a/data-access-object/README.md b/data-access-object/README.md index 526c598c6..18d232aef 100644 --- a/data-access-object/README.md +++ b/data-access-object/README.md @@ -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. diff --git a/data-bus/README.md b/data-bus/README.md index 0a193fa93..fecc899a2 100644 --- a/data-bus/README.md +++ b/data-bus/README.md @@ -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. diff --git a/data-locality/README.md b/data-locality/README.md index e27ee0ddf..bd92cb746 100644 --- a/data-locality/README.md +++ b/data-locality/README.md @@ -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 { diff --git a/data-mapper/README.md b/data-mapper/README.md index a6ce8894d..0bbdf5f87 100644 --- a/data-mapper/README.md +++ b/data-mapper/README.md @@ -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 diff --git a/decorator/README.md b/decorator/README.md index 171c81d41..0acd81111 100644 --- a/decorator/README.md +++ b/decorator/README.md @@ -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: diff --git a/double-buffer/README.md b/double-buffer/README.md index 85a56bcbd..1fef252d3 100644 --- a/double-buffer/README.md +++ b/double-buffer/README.md @@ -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> coordinateList) { LOGGER.info("Start drawing next frame"); LOGGER.info("Current buffer: " + current + " Next buffer: " + next); diff --git a/double-checked-locking/README.md b/double-checked-locking/README.md index b9dc43758..2bf7f0cd4 100644 --- a/double-checked-locking/README.md +++ b/double-checked-locking/README.md @@ -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. diff --git a/double-dispatch/README.md b/double-dispatch/README.md index 572133435..e7ef4465a 100644 --- a/double-dispatch/README.md +++ b/double-dispatch/README.md @@ -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) diff --git a/event-aggregator/README.md b/event-aggregator/README.md index 66320ba81..297e3a0cf 100644 --- a/event-aggregator/README.md +++ b/event-aggregator/README.md @@ -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> observerLists; diff --git a/event-based-asynchronous/README.md b/event-based-asynchronous/README.md index b2b76d648..c86e9ba16 100644 --- a/event-based-asynchronous/README.md +++ b/event-based-asynchronous/README.md @@ -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. diff --git a/event-driven-architecture/README.md b/event-driven-architecture/README.md index 674ffa776..298b4e4c5 100644 --- a/event-driven-architecture/README.md +++ b/event-driven-architecture/README.md @@ -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 diff --git a/event-sourcing/README.md b/event-sourcing/README.md index 8dc5ec430..acd9edacf 100644 --- a/event-sourcing/README.md +++ b/event-sourcing/README.md @@ -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. diff --git a/facade/README.md b/facade/README.md index 0bc261888..87b7cbdb6 100644 --- a/facade/README.md +++ b/facade/README.md @@ -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 diff --git a/factory-method/README.md b/factory-method/README.md index 160ffede8..a62c8c07c 100644 --- a/factory-method/README.md +++ b/factory-method/README.md @@ -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: diff --git a/factory/README.md b/factory/README.md index 8140a3387..4eb2d3503 100644 --- a/factory/README.md +++ b/factory/README.md @@ -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."; diff --git a/fanout-fanin/README.md b/fanout-fanin/README.md index 03999ce5c..f52bcd86a 100644 --- a/fanout-fanin/README.md +++ b/fanout-fanin/README.md @@ -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. diff --git a/filterer/README.md b/filterer/README.md index 07371d060..3d79ba30d 100644 --- a/filterer/README.md +++ b/filterer/README.md @@ -43,7 +43,9 @@ public interface Threat { int id(); ThreatType type(); } +``` +```java public interface ThreatAwareSystem { String systemId(); List 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. diff --git a/fluent-interface/README.md b/fluent-interface/README.md index d9fe457e1..d2660ac41 100644 --- a/fluent-interface/README.md +++ b/fluent-interface/README.md @@ -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 diff --git a/flyweight/README.md b/flyweight/README.md index 282b61927..328b788c0 100644 --- a/flyweight/README.md +++ b/flyweight/README.md @@ -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: diff --git a/game-loop/README.md b/game-loop/README.md index 4882eab92..f7aa2fcda 100644 --- a/game-loop/README.md +++ b/game-loop/README.md @@ -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. diff --git a/gateway/README.md b/gateway/README.md index 0440c2714..7761a78be 100644 --- a/gateway/README.md +++ b/gateway/README.md @@ -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. diff --git a/identity-map/README.md b/identity-map/README.md index 1737aa116..ca309e763 100644 --- a/identity-map/README.md +++ b/identity-map/README.md @@ -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) { diff --git a/iterator/README.md b/iterator/README.md index b36da68c7..4bebff534 100644 --- a/iterator/README.md +++ b/iterator/README.md @@ -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 diff --git a/leader-followers/README.md b/leader-followers/README.md index ce627345f..df3e790c5 100644 --- a/leader-followers/README.md +++ b/leader-followers/README.md @@ -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. diff --git a/log-aggregation/README.md b/log-aggregation/README.md index 36e32a57d..4873a8cac 100644 --- a/log-aggregation/README.md +++ b/log-aggregation/README.md @@ -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. diff --git a/model-view-intent/README.md b/model-view-intent/README.md index 983c3bcbb..20bca3413 100644 --- a/model-view-intent/README.md +++ b/model-view-intent/README.md @@ -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. diff --git a/model-view-presenter/README.md b/model-view-presenter/README.md index a6f8633b7..04e8c188b 100644 --- a/model-view-presenter/README.md +++ b/model-view-presenter/README.md @@ -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. diff --git a/mute-idiom/README.md b/mute-idiom/README.md index 0d38db506..0f58f9991 100644 --- a/mute-idiom/README.md +++ b/mute-idiom/README.md @@ -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. diff --git a/observer/README.md b/observer/README.md index f93d1f9c5..e2327d300 100644 --- a/observer/README.md +++ b/observer/README.md @@ -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: diff --git a/page-controller/README.md b/page-controller/README.md index 11a8d54c1..60d0e1d3a 100644 --- a/page-controller/README.md +++ b/page-controller/README.md @@ -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. diff --git a/parameter-object/README.md b/parameter-object/README.md index d02be23a3..fe23483f4 100644 --- a/parameter-object/README.md +++ b/parameter-object/README.md @@ -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 { diff --git a/queue-based-load-leveling/README.md b/queue-based-load-leveling/README.md index cc94cb4fa..ce11b4fac 100644 --- a/queue-based-load-leveling/README.md +++ b/queue-based-load-leveling/README.md @@ -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 diff --git a/role-object/README.md b/role-object/README.md index 54a6a8491..a45ffc237 100644 --- a/role-object/README.md +++ b/role-object/README.md @@ -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. diff --git a/saga/README.md b/saga/README.md index 33749fa14..72a6d369d 100644 --- a/saga/README.md +++ b/saga/README.md @@ -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. diff --git a/server-session/README.md b/server-session/README.md index ee3aeb1d7..924f63288 100644 --- a/server-session/README.md +++ b/server-session/README.md @@ -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 { diff --git a/service-to-worker/README.md b/service-to-worker/README.md index 2bd0d15e2..b9d444c02 100644 --- a/service-to-worker/README.md +++ b/service-to-worker/README.md @@ -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. diff --git a/spatial-partition/README.md b/spatial-partition/README.md index add0afc08..330cd4dc1 100644 --- a/spatial-partition/README.md +++ b/spatial-partition/README.md @@ -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. diff --git a/specification/README.md b/specification/README.md index ee011ab5c..4d67527c6 100644 --- a/specification/README.md +++ b/specification/README.md @@ -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.