diff --git a/composite/README.md b/composite/README.md index e44530f9c..f9d72ef9b 100644 --- a/composite/README.md +++ b/composite/README.md @@ -3,20 +3,27 @@ title: Composite category: Structural language: en tag: - - Gang of Four + - Gang of Four + - Object composition + - Recursion --- +## Also known as + +* Object Tree +* Composite Structure + ## Intent -Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients +Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. ## Explanation Real-world example -> Every sentence is composed of words which are in turn composed of characters. Each of these -> objects are printable and they can have something printed before or after them like sentence +> Every sentence is composed of words which are in turn composed of characters. Each of these +> objects are printable and they can have something printed before or after them like sentence > always ends with full stop and word always has space before it. In plain words @@ -25,85 +32,85 @@ In plain words Wikipedia says -> In software engineering, the composite pattern is a partitioning design pattern. The composite -> pattern describes that a group of objects is to be treated in the same way as a single instance of -> an object. The intent of a composite is to "compose" objects into tree structures to represent -> part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects +> In software engineering, the composite pattern is a partitioning design pattern. The composite +> pattern describes that a group of objects is to be treated in the same way as a single instance of +> an object. The intent of a composite is to "compose" objects into tree structures to represent +> part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects > and compositions uniformly. **Programmatic Example** -Taking our sentence example from above. Here we have the base class `LetterComposite` and the -different printable types `Letter`, `Word` and `Sentence`. +Taking our sentence example from above. Here we have the base class `LetterComposite` and the +different printable types `Letter`, `Word` and `Sentence`. ```java public abstract class LetterComposite { - private final List children = new ArrayList<>(); + private final List children = new ArrayList<>(); - public void add(LetterComposite letter) { - children.add(letter); - } + public void add(LetterComposite letter) { + children.add(letter); + } - public int count() { - return children.size(); - } + public int count() { + return children.size(); + } - protected void printThisBefore() { - } + protected void printThisBefore() { + } - protected void printThisAfter() { - } + protected void printThisAfter() { + } - public void print() { - printThisBefore(); - children.forEach(LetterComposite::print); - printThisAfter(); - } + public void print() { + printThisBefore(); + children.forEach(LetterComposite::print); + printThisAfter(); + } } public class Letter extends LetterComposite { - private final char character; + private final char character; - public Letter(char c) { - this.character = c; - } + public Letter(char c) { + this.character = c; + } - @Override - protected void printThisBefore() { - System.out.print(character); - } + @Override + protected void printThisBefore() { + System.out.print(character); + } } public class Word extends LetterComposite { - public Word(List letters) { - letters.forEach(this::add); - } - - public Word(char... letters) { - for (char letter : letters) { - this.add(new Letter(letter)); + public Word(List letters) { + letters.forEach(this::add); } - } - @Override - protected void printThisBefore() { - System.out.print(" "); - } + public Word(char... letters) { + for (char letter : letters) { + this.add(new Letter(letter)); + } + } + + @Override + protected void printThisBefore() { + System.out.print(" "); + } } public class Sentence extends LetterComposite { - public Sentence(List words) { - words.forEach(this::add); - } + public Sentence(List words) { + words.forEach(this::add); + } - @Override - protected void printThisAfter() { - System.out.print("."); - } + @Override + protected void printThisAfter() { + System.out.print("."); + } } ``` @@ -112,38 +119,38 @@ Then we have a messenger to carry messages: ```java public class Messenger { - LetterComposite messageFromOrcs() { + LetterComposite messageFromOrcs() { - var words = List.of( - new Word('W', 'h', 'e', 'r', 'e'), - new Word('t', 'h', 'e', 'r', 'e'), - new Word('i', 's'), - new Word('a'), - new Word('w', 'h', 'i', 'p'), - new Word('t', 'h', 'e', 'r', 'e'), - new Word('i', 's'), - new Word('a'), - new Word('w', 'a', 'y') - ); + var words = List.of( + new Word('W', 'h', 'e', 'r', 'e'), + new Word('t', 'h', 'e', 'r', 'e'), + new Word('i', 's'), + new Word('a'), + new Word('w', 'h', 'i', 'p'), + new Word('t', 'h', 'e', 'r', 'e'), + new Word('i', 's'), + new Word('a'), + new Word('w', 'a', 'y') + ); - return new Sentence(words); + return new Sentence(words); - } + } - LetterComposite messageFromElves() { + LetterComposite messageFromElves() { - var words = List.of( - new Word('M', 'u', 'c', 'h'), - new Word('w', 'i', 'n', 'd'), - new Word('p', 'o', 'u', 'r', 's'), - new Word('f', 'r', 'o', 'm'), - new Word('y', 'o', 'u', 'r'), - new Word('m', 'o', 'u', 't', 'h') - ); + var words = List.of( + new Word('M', 'u', 'c', 'h'), + new Word('w', 'i', 'n', 'd'), + new Word('p', 'o', 'u', 'r', 's'), + new Word('f', 'r', 'o', 'm'), + new Word('y', 'o', 'u', 'r'), + new Word('m', 'o', 'u', 't', 'h') + ); - return new Sentence(words); + return new Sentence(words); - } + } } ``` @@ -151,13 +158,13 @@ public class Messenger { And then it can be used as: ```java -var messenger = new Messenger(); +var messenger=new Messenger(); -LOGGER.info("Message from the orcs: "); -messenger.messageFromOrcs().print(); + LOGGER.info("Message from the orcs: "); + messenger.messageFromOrcs().print(); -LOGGER.info("Message from the elves: "); -messenger.messageFromElves().print(); + LOGGER.info("Message from the elves: "); + messenger.messageFromElves().print(); ``` The console output: @@ -178,16 +185,44 @@ Message from the elves: Use the Composite pattern when * You want to represent part-whole hierarchies of objects. -* You want clients to be able to ignore the difference between compositions of objects and -individual objects. Clients will treat all objects in the composite structure uniformly. +* You want clients to be able to ignore the difference between compositions of objects and + individual objects. Clients will treat all objects in the composite structure uniformly. ## Known uses -* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html) -* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) +* Graphical user interfaces where components can contain other components (e.g., panels containing buttons, labels, + other panels). +* File system representations where directories can contain files and other directories. +* Organizational structures where a department can contain sub-departments and employees. +* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) + and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html) +* [Apache Wicket](https://github.com/apache/wicket) component tree, + see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) + and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) + +## Consequences + +Benefits: + +* Simplifies client code, as it can treat composite structures and individual objects uniformly. +* Makes it easier to add new kinds of components, as existing code doesn't need to be changed. + +Trade-offs: + +* Can make the design overly general. It might be difficult to restrict the components of a composite. +* Can make it harder to restrict the types of components in a composite. + +## Related Patterns + +* [Flyweight](https://java-design-patterns.com/patterns/flyweight/): Composite can use Flyweight to share component + instances among several composites. +* [Iterator](https://java-design-patterns.com/patterns/iterator/): Can be used to traverse Composite structures. +* [Visitor](https://java-design-patterns.com/patterns/visitor/): Can apply an operation over a Composite structure. ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) * [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) * [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) +* [Pattern-Oriented Software Architecture, Volume 1: A System of Patterns](https://amzn.to/3xoLAmi) +* [Patterns of Enterprise Application Architecture](https://amzn.to/3vBKXWb)