mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-14 12:58:37 +00:00
docs: update Composite docs
This commit is contained in:
+122
-87
@@ -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<LetterComposite> children = new ArrayList<>();
|
||||
private final List<LetterComposite> 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<Letter> letters) {
|
||||
letters.forEach(this::add);
|
||||
}
|
||||
|
||||
public Word(char... letters) {
|
||||
for (char letter : letters) {
|
||||
this.add(new Letter(letter));
|
||||
public Word(List<Letter> 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<Word> words) {
|
||||
words.forEach(this::add);
|
||||
}
|
||||
public Sentence(List<Word> 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)
|
||||
|
||||
Reference in New Issue
Block a user