From b8f5d46ba71ee19a5ce3c450e1d28c6e14f2e419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 7 Apr 2024 18:06:46 +0300 Subject: [PATCH] docs: update delegation --- delegation/README.md | 142 +++++++++++------- .../com/iluwatar/delegation/simple/App.java | 4 +- .../delegation/simple/PrinterController.java | 4 +- .../simple/printers/EpsonPrinter.java | 2 +- .../delegation/simple/printers/HpPrinter.java | 2 +- .../iluwatar/delegation/simple/AppTest.java | 1 - .../delegation/simple/DelegateTest.java | 6 +- 7 files changed, 95 insertions(+), 66 deletions(-) diff --git a/delegation/README.md b/delegation/README.md index def547ec6..7774c5694 100644 --- a/delegation/README.md +++ b/delegation/README.md @@ -1,34 +1,29 @@ --- title: Delegation -category: Structural +category: Behavioral language: en tag: - - Decoupling + - Delegation --- ## Also known as -Proxy Pattern + +* Helper +* Surrogate ## Intent -It is a technique where an object expresses certain behavior to the outside but in -reality delegates responsibility for implementing that behaviour to an associated object. + +The Delegation pattern in Java allows an object to delegate one or more tasks to a helper object. It is a technique where an object expresses certain behavior but actually delegates responsibility for implementing that behavior to an associated helper object. ## Explanation Real-world example -> Imagine that we have adventurers who fight monsters with different weapons depending on their -> abilities and skills. We must be able to equip them with different ones without having to -> modify their source code for each one. The delegation pattern makes it possible by delegating -> the dynamic work to a specific object implementing an interface with relevant methods. +> In a restaurant, the head chef delegates tasks to sous-chefs: one manages grilling, another handles salads, and a third is in charge of desserts. Each sous-chef specializes in their area, allowing the head chef to focus on overall kitchen management. This mirrors the Delegation design pattern, where a main object delegates specific tasks to helper objects, each expert in their domain. Wikipedia says -> In object-oriented programming, delegation refers to evaluating a member (property or method) of -> one object (the receiver) in the context of another original object (the sender). Delegation can -> be done explicitly, by passing the sending object to the receiving object, which can be done in -> any object-oriented language; or implicitly, by the member lookup rules of the language, which -> requires language support for the feature. +> In object-oriented programming, delegation refers to evaluating a member (property or method) of one object (the receiver) in the context of another original object (the sender). Delegation can be done explicitly, by passing the sending object to the receiving object, which can be done in any object-oriented language; or implicitly, by the member lookup rules of the language, which requires language support for the feature. **Programmatic Example** @@ -36,84 +31,119 @@ We have an interface `Printer` and three implementations `CanonPrinter`, `EpsonP ```java public interface Printer { - void print(final String message); + void print(final String message); } @Slf4j public class CanonPrinter implements Printer { - @Override - public void print(String message) { - LOGGER.info("Canon Printer : {}", message); - } + @Override + public void print(String message) { + LOGGER.info("Canon Printer : {}", message); + } } @Slf4j public class EpsonPrinter implements Printer { - @Override - public void print(String message) { - LOGGER.info("Epson Printer : {}", message); - } + @Override + public void print(String message) { + LOGGER.info("Epson Printer : {}", message); + } } @Slf4j public class HpPrinter implements Printer { - @Override - public void print(String message) { - LOGGER.info("HP Printer : {}", message); - } + @Override + public void print(String message) { + LOGGER.info("HP Printer : {}", message); + } } ``` -The `PrinterController` can be used as a `Printer` by delegating any work handled by this + +The `PrinterController` can be used as a `Printer` by delegating any work handled by this interface to an object implementing it. + ```java public class PrinterController implements Printer { - - private final Printer printer; - - public PrinterController(Printer printer) { - this.printer = printer; - } - - @Override - public void print(String message) { - printer.print(message); - } + + private final Printer printer; + + public PrinterController(Printer printer) { + this.printer = printer; + } + + @Override + public void print(String message) { + printer.print(message); + } } ``` Now on the client code printer controllers can print messages differently depending on the -object they're delegating that work to. +object they're delegating that work to. ```java -private static final String MESSAGE_TO_PRINT = "hello world"; +public class App { -var hpPrinterController = new PrinterController(new HpPrinter()); -var canonPrinterController = new PrinterController(new CanonPrinter()); -var epsonPrinterController = new PrinterController(new EpsonPrinter()); + private static final String MESSAGE_TO_PRINT = "hello world"; -hpPrinterController.print(MESSAGE_TO_PRINT); -canonPrinterController.print(MESSAGE_TO_PRINT); -epsonPrinterController.print(MESSAGE_TO_PRINT) + public static void main(String[] args) { + var hpPrinterController = new PrinterController(new HpPrinter()); + var canonPrinterController = new PrinterController(new CanonPrinter()); + var epsonPrinterController = new PrinterController(new EpsonPrinter()); + + hpPrinterController.print(MESSAGE_TO_PRINT); + canonPrinterController.print(MESSAGE_TO_PRINT); + epsonPrinterController.print(MESSAGE_TO_PRINT); + } +} ``` Program output: -```java -HP Printer : hello world -Canon Printer : hello world -Epson Printer : hello world +``` +HP Printer:hello world +Canon Printer:hello world +Epson Printer:hello world ``` ## Class diagram -![alt text](./etc/delegation.png "Delegate") + +![Delegate class diagram](./etc/delegation.png "Delegate") ## Applicability -Use the Delegate pattern in order to achieve the following -* Reduce the coupling of methods to their class -* Components that behave identically, but realize that this situation can change in the future. +* When you want to pass responsibility from one class to another without inheritance. +* To achieve composition-based reuse instead of inheritance-based. +* When you need to use several interchangeable helper classes at runtime. + +## Known Uses + +* Java's java.awt.event package, where listeners are often used to handle events. +* Wrapper classes in Java's Collections Framework (java.util.Collections), which delegate to other collection objects. +* In Spring Framework, delegation is used extensively in the IoC container where beans delegate tasks to other beans. + +## Consequences + +Benefits: + +* Reduces subclassing: Objects can delegate operations to different objects and change them at runtime, reducing the need for subclassing. +* Encourages reuse: Delegation promotes the reuse of the helper object's code. +* Increases flexibility: By delegating tasks to helper objects, you can change the behavior of your classes at runtime. + +Trade-offs: + +* Runtime Overhead: Delegation can introduce additional layers of indirection, which may result in slight performance costs. +* Complexity: The design can become more complicated since it involves additional classes and interfaces to manage delegation. + +## Related Patterns + +* [Composite](https://java-design-patterns.com/patterns/composite/): Delegation can be used within a composite pattern to delegate component-specific behavior to child components. +* [Strategy](https://java-design-patterns.com/patterns/strategy/): Delegation is often used in the strategy pattern where a context object delegates tasks to a strategy object. +* https://java-design-patterns.com/patterns/proxy/: The proxy pattern is a form of delegation where a proxy object controls access to another object, which it delegates work to. ## Credits +* [Effective Java](https://amzn.to/4aGE7gX) +* [Head First Design Patterns](https://amzn.to/3J9tuaB) +* [Refactoring: Improving the Design of Existing Code](https://amzn.to/3VOcRsw) * [Delegate Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Delegation_pattern) -* [Proxy Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Proxy_pattern) diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/App.java b/delegation/src/main/java/com/iluwatar/delegation/simple/App.java index f81e033bd..7986643c6 100644 --- a/delegation/src/main/java/com/iluwatar/delegation/simple/App.java +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/App.java @@ -38,9 +38,9 @@ import com.iluwatar.delegation.simple.printers.HpPrinter; * *

In this example the delegates are {@link EpsonPrinter}, {@link HpPrinter} and {@link * CanonPrinter} they all implement {@link Printer}. The {@link PrinterController} class also - * implements {@link Printer}. However neither provide the functionality of {@link Printer} by + * implements {@link Printer}. However, neither provide the functionality of {@link Printer} by * printing to the screen, they actually call upon the instance of {@link Printer} that they were - * instantiated with. Therefore delegating the behaviour to another class. + * instantiated with. Therefore, delegating the behaviour to another class. */ public class App { diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java b/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java index 8e9a3896d..28488d485 100644 --- a/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java @@ -27,7 +27,7 @@ package com.iluwatar.delegation.simple; /** * Delegator Class to delegate the implementation of the Printer. This ensures two things: - when * the actual implementation of the Printer class changes the delegation will still be operational - - * the actual benefit is observed when there are more than one implementors and they share a + * the actual benefit is observed when there are more than one implementor, and they share a * delegation control */ public class PrinterController implements Printer { @@ -41,7 +41,7 @@ public class PrinterController implements Printer { /** * This method is implemented from {@link Printer} however instead on providing an implementation, * it instead calls upon the class passed through the constructor. This is the delegate, hence the - * pattern. Therefore meaning that the caller does not care of the implementing class only the + * pattern. Therefore, meaning that the caller does not care of the implementing class only the * owning controller. * * @param message to be printed to the screen diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java index 88240a806..4fcac417f 100644 --- a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java @@ -28,7 +28,7 @@ import com.iluwatar.delegation.simple.Printer; import lombok.extern.slf4j.Slf4j; /** - * Specialised Implementation of {@link Printer} for a Epson Printer, in this case the message to be + * Specialised Implementation of {@link Printer} for an Epson Printer, in this case the message to be * printed is appended to "Epson Printer : ". * * @see Printer diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java index 63a4ccbae..1705a775e 100644 --- a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java @@ -28,7 +28,7 @@ import com.iluwatar.delegation.simple.Printer; import lombok.extern.slf4j.Slf4j; /** - * Specialised Implementation of {@link Printer} for a HP Printer, in this case the message to be + * Specialised Implementation of {@link Printer} for an HP Printer, in this case the message to be * printed is appended to "HP Printer : ". * * @see Printer diff --git a/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java b/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java index e0dcf31c4..cd57181c2 100644 --- a/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java +++ b/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java @@ -35,7 +35,6 @@ class AppTest { /** * Issue: Add at least one assertion to this test case. - * * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])} * throws an exception. */ diff --git a/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java b/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java index 10d896cfa..4c0273f68 100644 --- a/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java +++ b/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java @@ -59,7 +59,7 @@ class DelegateTest { private static final String MESSAGE = "Test Message Printed"; @Test - void testCanonPrinter() throws Exception { + void testCanonPrinter() { var printerController = new PrinterController(new CanonPrinter()); printerController.print(MESSAGE); @@ -67,7 +67,7 @@ class DelegateTest { } @Test - void testHpPrinter() throws Exception { + void testHpPrinter() { var printerController = new PrinterController(new HpPrinter()); printerController.print(MESSAGE); @@ -75,7 +75,7 @@ class DelegateTest { } @Test - void testEpsonPrinter() throws Exception { + void testEpsonPrinter() { var printerController = new PrinterController(new EpsonPrinter()); printerController.print(MESSAGE);