diff --git a/step-builder/README.md b/step-builder/README.md index c78537dae..9a18ecda2 100644 --- a/step-builder/README.md +++ b/step-builder/README.md @@ -33,20 +33,10 @@ Wikipedia says > The Step Builder pattern is a variation of the Builder design pattern, designed to provide a flexible solution for constructing complex objects step-by-step. This pattern is particularly useful when an object requires multiple initialization steps, which can be done incrementally to ensure clarity and flexibility in the creation process. -## Programmatic Example - -Sure, let's create a programmatic example of the Step Builder design pattern using the code from the `step-builder` module. - -## Step Builder Design Pattern +**Programmatic Example** The Step Builder pattern is an extension of the Builder pattern that guides the user through the creation of an object in a step-by-step manner. This pattern improves the user experience by only showing the next step methods available, and not showing the build method until it's the right time to build the object. -### Class Diagram - -![Step Builder](./etc/step-builder.png "Step Builder") - -### Code Example - Let's consider a `Character` class that has many attributes such as `name`, `fighterClass`, `wizardClass`, `weapon`, `spell`, and `abilities`. ```java diff --git a/strangler/README.md b/strangler/README.md index b7e3e611f..1e687a123 100644 --- a/strangler/README.md +++ b/strangler/README.md @@ -3,26 +3,158 @@ title: Strangler category: Structural language: en tag: - - Extensibility - - Cloud distributed + - Migration + - Modernization + - Refactoring --- +## Also known as + +* Strangler Fig + ## Intent -Incrementally migrate a legacy system by gradually replacing specific pieces of functionality -with new applications and services. As features from the legacy system are replaced, the new -system eventually covers all the old system's features and may has its own new features, then -strangling the old system and allowing you to decommission it. + +Incrementally replace the legacy system by building a new system alongside the old one, eventually strangling the old system. + +## Explanation + +Real-world example + +> Imagine a city planning department that decides to modernize an old bridge that's crucial for daily commutes. Instead of demolishing the old bridge and causing major disruptions, they build a new, modern bridge next to it. As sections of the new bridge are completed, traffic is gradually diverted from the old bridge to the new one. Eventually, the entire flow of traffic moves to the new bridge, and the old bridge is either decommissioned or demolished. This way, the transition is smooth, and the city's daily activities are minimally affected. This approach mirrors the Strangler Design Pattern, where a legacy system is incrementally replaced by a new system, ensuring continuous operation during the transition. + +In plain words + +> The Strangler Design Pattern incrementally replaces a legacy system by developing a new system alongside it and gradually migrating functionality until the old system is entirely replaced. + +Wikipedia says + +> The Strangler Design Pattern involves incrementally migrating a legacy system by gradually replacing it with a new system. It wraps old code with new code, redirecting or logging uses of the old code to ensure a seamless transition. This pattern is named after the strangler fig plant, which grows around a host tree and eventually replaces it entirely. It's particularly useful for modernizing monolithic applications and transitioning them to microservices architecture with minimal risk and disruption. + +**Programmatic Example** + +The Strangler design pattern is a software design pattern that incrementally migrates a legacy system by gradually replacing specific pieces of functionality with new applications and services. As features from the legacy system are replaced, the new system eventually replaces all of the old system's features, strangling the old system and allowing you to decommission it. + +In the provided code, we have an example of the Strangler pattern in action. The `OldArithmetic` class represents the legacy system, while the `HalfArithmetic` and `NewArithmetic` classes represent the new system at different stages of development. + +Let's break down the code to understand how the Strangler pattern is implemented. + +```java +public class OldArithmetic { + private final OldSource source; + + public OldArithmetic(OldSource source) { + this.source = source; + } + + // The sum and mul methods represent the functionality of the legacy system. + public int sum(int... nums) { + return source.accumulateSum(nums); + } + + public int mul(int... nums) { + return source.accumulateMul(nums); + } +} +``` + +The `OldArithmetic` class represents the legacy system. It has two methods, `sum` and `mul`, which depend on the `OldSource` class. + +```java +public class HalfArithmetic { + private final HalfSource newSource; + private final OldSource oldSource; + + public HalfArithmetic(HalfSource newSource, OldSource oldSource) { + this.newSource = newSource; + this.oldSource = oldSource; + } + + // The sum method has been migrated to use the new source. + public int sum(int... nums) { + return newSource.accumulateSum(nums); + } + + // The mul method still uses the old source. + public int mul(int... nums) { + return oldSource.accumulateMul(nums); + } + + // The ifHasZero method is a new feature added in the new system. + public boolean ifHasZero(int... nums) { + return !newSource.ifNonZero(nums); + } +} +``` + +The `HalfArithmetic` class represents the system during the migration process. It depends on both the `OldSource` and `HalfSource` classes. The `sum` method has been migrated to use the new source, while the `mul` method still uses the old source. The `ifHasZero` method is a new feature added in the new system. + +```java +public class NewArithmetic { + private final NewSource source; + + public NewArithmetic(NewSource source) { + this.source = source; + } + + // All methods now use the new source. + public int sum(int... nums) { + return source.accumulateSum(nums); + } + + public int mul(int... nums) { + return source.accumulateMul(nums); + } + + public boolean ifHasZero(int... nums) { + return !source.ifNonZero(nums); + } +} +``` + +The `NewArithmetic` class represents the system after the migration process. It only depends on the `NewSource` class. All methods now use the new source. + +This is a typical example of the Strangler pattern. The legacy system (`OldArithmetic`) is gradually replaced by the new system (`HalfArithmetic` and `NewArithmetic`). The new system is developed incrementally, and at each stage, it strangles a part of the legacy system until the legacy system is completely replaced. ## Class diagram -![alt text](./etc/strangler.png "Strangler") + +![Strangler](./etc/strangler.png "Strangler") ## Applicability -This strangler pattern is a safe way to phase one thing out for something better, cheaper, or -more expandable. Especially when you want to update legacy system with new techniques and need -continuously develop new features at the same time. Note that this pattern indeed need extra effort, -so usually use it when the system is not so simple. + +* Use when you need to replace a monolithic or legacy system incrementally. +* Ideal for scenarios where the system cannot be replaced in one go due to risk or complexity. +* Suitable when you need to modernize parts of an application while ensuring continuous operation. + +## Known Uses + +* Replacing a legacy monolithic application with a microservices architecture. +* Transitioning from an on-premise system to a cloud-based system. +* Incrementally migrating from an old database schema to a new one without downtime. + +## Consequences + +Benefits: + +* Reduces risk by allowing gradual replacement. +* Enables continuous delivery and operation during migration. +* Allows for testing and validating new components before full replacement. + +Trade-offs: + +* Requires managing interactions between new and old systems, which can be complex. +* May introduce temporary performance overhead due to coexistence of old and new systems. +* Potentially increases the initial development time due to the need for integration. + +## Related Patterns + +* [Adapter](https://java-design-patterns.com/patterns/adapter/): Used to make new systems interact with the old system during the transition period. +* [Facade](https://java-design-patterns.com/patterns/facade/): Can provide a unified interface to the old and new systems, simplifying client interactions. +* Microservices: The target architecture in many cases where the Strangler Pattern is applied. ## Credits -* [Strangler pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/strangler) -* [Legacy Application Strangulation : Case Studies](https://paulhammant.com/2013/07/14/legacy-application-strangulation-case-studies/) +* [Building Microservices](https://amzn.to/3UACtrU) +* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR) +* [Refactoring: Improving the Design of Existing Code](https://amzn.to/3TVEgaB) +* [Strangler pattern - Microsoft](https://docs.microsoft.com/en-us/azure/architecture/patterns/strangler) +* [Legacy Application Strangulation: Case Studies - Paul Hammant](https://paulhammant.com/2013/07/14/legacy-application-strangulation-case-studies/) diff --git a/strangler/src/main/java/com/iluwatar/strangler/HalfArithmetic.java b/strangler/src/main/java/com/iluwatar/strangler/HalfArithmetic.java index b9ff85df0..161ec0655 100644 --- a/strangler/src/main/java/com/iluwatar/strangler/HalfArithmetic.java +++ b/strangler/src/main/java/com/iluwatar/strangler/HalfArithmetic.java @@ -63,9 +63,9 @@ public class HalfArithmetic { } /** - * Chech if has any zero. + * Check if it has any zero. * @param nums numbers need to check - * @return if has any zero, return true, else, return false + * @return if it has any zero, return true, else, return false */ public boolean ifHasZero(int... nums) { LOGGER.info("Arithmetic check zero {}", VERSION); diff --git a/strangler/src/main/java/com/iluwatar/strangler/NewArithmetic.java b/strangler/src/main/java/com/iluwatar/strangler/NewArithmetic.java index 248e1f469..bd90fc123 100644 --- a/strangler/src/main/java/com/iluwatar/strangler/NewArithmetic.java +++ b/strangler/src/main/java/com/iluwatar/strangler/NewArithmetic.java @@ -60,9 +60,9 @@ public class NewArithmetic { } /** - * Chech if has any zero. + * Check if it has any zero. * @param nums numbers need to check - * @return if has any zero, return true, else, return false + * @return if it has any zero, return true, else, return false */ public boolean ifHasZero(int... nums) { LOGGER.info("Arithmetic check zero {}", VERSION);