docs: update Composite docs

This commit is contained in:
Ilkka Seppälä committed 2024-03-30 21:30:52 +02:00
1 parent 48a5edef0e
commit e00e67c9a6
1 file changed
+112 -77
+112 -77
View File
@@ -3,9 +3,16 @@ title: Composite
category: Structural category: Structural
language: en language: en
tag: tag:
- Gang of Four - Gang of Four
- Object composition
- Recursion
--- ---
## Also known as
* Object Tree
* Composite Structure
## Intent ## 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
@@ -39,71 +46,71 @@ different printable types `Letter`, `Word` and `Sentence`.
```java ```java
public abstract class LetterComposite { public abstract class LetterComposite {
private final List<LetterComposite> children = new ArrayList<>(); private final List<LetterComposite> children = new ArrayList<>();
public void add(LetterComposite letter) { public void add(LetterComposite letter) {
children.add(letter); children.add(letter);
} }
public int count() { public int count() {
return children.size(); return children.size();
} }
protected void printThisBefore() { protected void printThisBefore() {
} }
protected void printThisAfter() { protected void printThisAfter() {
} }
public void print() { public void print() {
printThisBefore(); printThisBefore();
children.forEach(LetterComposite::print); children.forEach(LetterComposite::print);
printThisAfter(); printThisAfter();
} }
} }
public class Letter extends LetterComposite { public class Letter extends LetterComposite {
private final char character; private final char character;
public Letter(char c) { public Letter(char c) {
this.character = c; this.character = c;
} }
@Override @Override
protected void printThisBefore() { protected void printThisBefore() {
System.out.print(character); System.out.print(character);
} }
} }
public class Word extends LetterComposite { public class Word extends LetterComposite {
public Word(List<Letter> letters) { public Word(List<Letter> letters) {
letters.forEach(this::add); letters.forEach(this::add);
}
public Word(char... letters) {
for (char letter : letters) {
this.add(new Letter(letter));
} }
}
@Override public Word(char... letters) {
protected void printThisBefore() { for (char letter : letters) {
System.out.print(" "); this.add(new Letter(letter));
} }
}
@Override
protected void printThisBefore() {
System.out.print(" ");
}
} }
public class Sentence extends LetterComposite { public class Sentence extends LetterComposite {
public Sentence(List<Word> words) { public Sentence(List<Word> words) {
words.forEach(this::add); words.forEach(this::add);
} }
@Override @Override
protected void printThisAfter() { protected void printThisAfter() {
System.out.print("."); System.out.print(".");
} }
} }
``` ```
@@ -112,38 +119,38 @@ Then we have a messenger to carry messages:
```java ```java
public class Messenger { public class Messenger {
LetterComposite messageFromOrcs() { LetterComposite messageFromOrcs() {
var words = List.of( var words = List.of(
new Word('W', 'h', 'e', 'r', 'e'), new Word('W', 'h', 'e', 'r', 'e'),
new Word('t', 'h', 'e', 'r', 'e'), new Word('t', 'h', 'e', 'r', 'e'),
new Word('i', 's'), new Word('i', 's'),
new Word('a'), new Word('a'),
new Word('w', 'h', 'i', 'p'), new Word('w', 'h', 'i', 'p'),
new Word('t', 'h', 'e', 'r', 'e'), new Word('t', 'h', 'e', 'r', 'e'),
new Word('i', 's'), new Word('i', 's'),
new Word('a'), new Word('a'),
new Word('w', 'a', 'y') new Word('w', 'a', 'y')
); );
return new Sentence(words); return new Sentence(words);
} }
LetterComposite messageFromElves() { LetterComposite messageFromElves() {
var words = List.of( var words = List.of(
new Word('M', 'u', 'c', 'h'), new Word('M', 'u', 'c', 'h'),
new Word('w', 'i', 'n', 'd'), new Word('w', 'i', 'n', 'd'),
new Word('p', 'o', 'u', 'r', 's'), new Word('p', 'o', 'u', 'r', 's'),
new Word('f', 'r', 'o', 'm'), new Word('f', 'r', 'o', 'm'),
new Word('y', 'o', 'u', 'r'), new Word('y', 'o', 'u', 'r'),
new Word('m', 'o', 'u', 't', 'h') 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: And then it can be used as:
```java ```java
var messenger = new Messenger(); var messenger=new Messenger();
LOGGER.info("Message from the orcs: "); LOGGER.info("Message from the orcs: ");
messenger.messageFromOrcs().print(); messenger.messageFromOrcs().print();
LOGGER.info("Message from the elves: "); LOGGER.info("Message from the elves: ");
messenger.messageFromElves().print(); messenger.messageFromElves().print();
``` ```
The console output: The console output:
@@ -179,15 +186,43 @@ Use the Composite pattern when
* You want to represent part-whole hierarchies of objects. * You want to represent part-whole hierarchies of objects.
* You want clients to be able to ignore the difference between compositions of objects and * 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. individual objects. Clients will treat all objects in the composite structure uniformly.
## Known uses ## 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) * Graphical user interfaces where components can contain other components (e.g., panels containing buttons, labels,
* [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) 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 ## 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) * [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) * [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) * [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)