diff --git a/twin/README.md b/twin/README.md index 338542a99..00d95fede 100644 --- a/twin/README.md +++ b/twin/README.md @@ -3,71 +3,61 @@ title: Twin category: Structural language: en tag: - - Extensibility + - Decoupling + - Object composition + - Performance + - Resilience --- ## Intent -Twin pattern is a design pattern which provides a standard solution to simulate multiple -inheritance in java + +To provide a way to handle multiple, related classes in a manner that allows them to work together without inheriting from a common base class. ## Explanation Real-world example -> Consider a game with a ball that needs features of two types, Game Item, and threads to function -> smoothly in the game. We can use two objects, with one object compatible with the first type and -> the other compatible with the second type. -> The pair of objects together can function as one ball in the game. +> An analogous real-world example of the Twin design pattern can be found in the relationship between a driver and a driving simulator. Imagine a driver (the first class) and a driving simulator (the second class) that both need to interact with the same set of vehicle controls (steering, acceleration, braking) and receive the same feedback (speed, engine status). +> +> Despite performing similar functions, the driver and the simulator cannot share a common base class because they operate in fundamentally different environments—one in the physical world and the other in a virtual environment. Instead, they are "twinned" to ensure consistent interaction with the vehicle controls and feedback mechanisms. This setup allows improvements or changes to be made to the simulator without affecting the driver and vice versa, maintaining the system's overall flexibility and resilience. In plain words -> It provides a way to form two closely coupled sub-classes that can act as a twin class having two ends. +> It provides a way to form two closely coupled subclasses that can act as a twin class having two ends. Wikipedia says -> In software engineering, the Twin pattern is a software design pattern that allows developers -> to model multiple inheritance in programming languages that do not support multiple inheritance. -> This pattern avoids many of the problems with multiple inheritance. +> The Twin pattern is a software design pattern that allows developers to simulate multiple inheritance in languages that don't support it. Instead of creating a single class inheriting from multiple parents, two closely linked subclasses are created, each inheriting from one of the parents. These subclasses are mutually dependent, working together as a pair to achieve the desired functionality. This approach avoids the complications and inefficiencies often associated with multiple inheritance, while still allowing the reuse of functionalities from different classes. **Programmatic Example** -Take our game ball example from above. Consider we have a game in which the ball needs to be both a `GameItem` and `Thread`. -First of all, we have the `GameItem` class given below and the `Thread` class. +Consider a game where a ball needs to function as both a `GameItem` and a `Thread`. Instead of inheriting from both, we use the Twin pattern with two closely linked objects: `BallItem` and `BallThread`. +Here is the `GameItem` class: ```java - @Slf4j public abstract class GameItem { - public void draw() { LOGGER.info("draw"); doDraw(); } - public abstract void doDraw(); - - public abstract void click(); } - ``` -Then, we have subclasses `BallItem` and `BallThread` inheriting them, respectively. +`BallItem` and `BallThread` subclasses: ```java - @Slf4j public class BallItem extends GameItem { - private boolean isSuspended; - @Setter private BallThread twin; @Override public void doDraw() { - LOGGER.info("doDraw"); } @@ -77,9 +67,7 @@ public class BallItem extends GameItem { @Override public void click() { - isSuspended = !isSuspended; - if (isSuspended) { twin.suspendMe(); } else { @@ -88,22 +76,14 @@ public class BallItem extends GameItem { } } - @Slf4j public class BallThread extends Thread { - @Setter private BallItem twin; - private volatile boolean isSuspended; - private volatile boolean isRunning = true; - /** - * Run the thread. - */ public void run() { - while (isRunning) { if (!isSuspended) { twin.draw(); @@ -132,31 +112,57 @@ public class BallThread extends Thread { this.isSuspended = true; } } +``` -``` - -Now, when we need the ball, we can instantiate objects from both the `BallThread` and `BallItem` as a pair and pass them to its pair object so they can act together as appropriate. +To use these classes together: ```java - var ballItem = new BallItem(); var ballThread = new BallThread(); ballItem.setTwin(ballThread); ballThread.setTwin(ballItem); - ``` +This setup allows `BallItem` and `BallThread` to act together as a single cohesive unit in the game, leveraging the capabilities of both `GameItem` and `Thread` without multiple inheritance. ## Class diagram -![alt text](./etc/twin.png "Twin") + +![Twin](./etc/twin.png "Twin") ## Applicability -Use the Twin idiom when -* To simulate multiple inheritance in a language that does not support this feature. -* To avoid certain problems of multiple inheritance such as name clashes. +* Use when you need to decouple classes that share common functionality but cannot inherit from a common base class due to various reasons such as the use of different frameworks or languages. +* Useful in performance-critical applications where inheritance might introduce unnecessary overhead. +* Applicable in systems requiring resilience through the ability to replace or update one of the twins without affecting the other. + +## Known Uses + +* User interfaces where different frameworks are used for rendering and logic. +* Systems integrating legacy code with new implementations where direct inheritance is not feasible. + +## Consequences + +Benefits: + +* Reduces coupling between classes, promoting modularity and easier maintenance. +* Improves flexibility and reuse of classes across different frameworks or languages. +* Enhances performance by avoiding the overhead associated with inheritance. + +Trade-offs: + +* Can lead to code duplication if not managed properly. +* Increased complexity in managing the interaction between twin classes. + +## Related Patterns + +* [Adapter](https://java-design-patterns.com/patterns/adapter/): Both patterns deal with compatibility issues, but Adapter focuses on converting interfaces while Twin deals with class collaboration without inheritance. +* [Bridge](https://java-design-patterns.com/patterns/bridge/): Similar in decoupling abstraction from implementation, but Twin specifically avoids inheritance. +* [Proxy](https://java-design-patterns.com/patterns/proxy/): Manages object access, similar to how Twin handles interaction, but Proxy typically focuses on control and logging. ## Credits -* [Twin – A Design Pattern for Modeling Multiple Inheritance](http://www.ssw.uni-linz.ac.at/Research/Papers/Moe99/Paper.pdf) +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) +* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525) +* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR) +* [Twin – A Design Pattern for Modeling Multiple Inheritance - Hanspeter Mössenböck](http://www.ssw.uni-linz.ac.at/Research/Papers/Moe99/Paper.pdf) diff --git a/twin/src/main/java/com/iluwatar/twin/BallItem.java b/twin/src/main/java/com/iluwatar/twin/BallItem.java index 64c34dca5..c1a0a2ddf 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallItem.java +++ b/twin/src/main/java/com/iluwatar/twin/BallItem.java @@ -29,7 +29,7 @@ import lombok.extern.slf4j.Slf4j; /** * This class represents a Ball which extends {@link GameItem} and implements the logic for ball - * item, like move and draw. It hold a reference of {@link BallThread} to delegate the suspend and + * item, like move and draw. It holds a reference of {@link BallThread} to delegate the suspend and * resume task. */ @Slf4j diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index c6989cd51..9d4d9cf71 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -29,7 +29,7 @@ import lombok.extern.slf4j.Slf4j; /** * This class is a UI thread for drawing the {@link BallItem}, and provide the method for suspend - * and resume. It hold the reference of {@link BallItem} to delegate the draw task. + * and resume. It holds the reference of {@link BallItem} to delegate the draw task. */ @Slf4j diff --git a/twin/src/test/java/com/iluwatar/twin/BallItemTest.java b/twin/src/test/java/com/iluwatar/twin/BallItemTest.java index d13ff6698..c8167065f 100644 --- a/twin/src/test/java/com/iluwatar/twin/BallItemTest.java +++ b/twin/src/test/java/com/iluwatar/twin/BallItemTest.java @@ -108,7 +108,7 @@ class BallItemTest { /** * Logging Appender Implementation */ - class InMemoryAppender extends AppenderBase { + static class InMemoryAppender extends AppenderBase { private final List log = new LinkedList<>(); public InMemoryAppender() {