Java 中的组合模式:构建灵活的树状结构
大约 3 分钟
也称为
- 对象树
- 组合结构
组合设计模式的意图
将对象组合成树状结构以表示部分-整体层次结构。组合设计模式允许客户端以统一的方式处理单个对象和对象组合。
组合模式的详细解释以及现实世界中的例子
现实世界中的例子
在现实世界的例子中,考虑一家具有复杂组织结构的公司。该公司由多个部门组成,每个部门都可以包含子部门,最终包含单个员工。组合设计模式可以用来表示这种结构。每个部门和员工都被视为树状结构中的一个节点,其中部门可以包含其他部门或员工,但员工是叶子节点,没有子节点。这允许公司以统一的方式执行操作,例如计算总工资或打印组织结构图,通过以相同的方式处理单个员工和整个部门。
通俗地说
组合设计模式允许客户端以统一的方式处理单个对象和对象组合。
维基百科说
在软件工程中,组合模式是一种分区设计模式。组合模式描述了一组对象应以与单个对象实例相同的方式处理。组合的目的是“组合”对象以形成树状结构以表示部分-整体层次结构。实现组合模式允许客户端以统一的方式处理单个对象和组合。
Java 中组合模式的编程示例
每个句子由单词组成,单词又由字符组成。这些对象都是可打印的,它们可以在其前后打印一些内容,例如句子总是以句号结尾,单词前面总是空格。
这里我们有基类 LetterComposite
和不同的可打印类型 Letter
、Word
和 Sentence
。
public abstract class LetterComposite {
private final List<LetterComposite> children = new ArrayList<>();
public void add(LetterComposite letter) {
children.add(letter);
}
public int count() {
return children.size();
}
protected void printThisBefore() {
}
protected void printThisAfter() {
}
public void print() {
printThisBefore();
children.forEach(LetterComposite::print);
printThisAfter();
}
}
public class Letter extends LetterComposite {
private final char character;
public Letter(char c) {
this.character = c;
}
@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));
}
}
@Override
protected void printThisBefore() {
System.out.print(" ");
}
}
public class Sentence extends LetterComposite {
public Sentence(List<Word> words) {
words.forEach(this::add);
}
@Override
protected void printThisAfter() {
System.out.print(".");
}
}
然后我们有一个传递消息的信使
public class Messenger {
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')
);
return new Sentence(words);
}
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')
);
return new Sentence(words);
}
}
然后它可以用作
public static void main(String[] args) {
var messenger = new Messenger();
LOGGER.info("Message from the orcs: ");
messenger.messageFromOrcs().print();
LOGGER.info("Message from the elves: ");
messenger.messageFromElves().print();
}
控制台输出
20:43:54.801 [main] INFO com.iluwatar.composite.App -- Message from the orcs:
Where there is a whip there is a way.
20:43:54.803 [main] INFO com.iluwatar.composite.App -- Message from the elves:
Much wind pours from your mouth.
何时在 Java 中使用组合模式
在以下情况下使用组合模式
- 您想表示对象的“部分-整体”层次结构。
- 您希望客户端能够忽略对象组合和单个对象之间的差异。客户端将以统一的方式处理组合结构中的所有对象。
Java 中组合模式的现实世界应用
- 图形用户界面,其中组件可以包含其他组件(例如,包含按钮、标签和其他面板的面板)。
- 文件系统表示,其中目录可以包含文件和其他目录。
- 组织结构,其中部门可以包含子部门和员工。
- java.awt.Container 和 java.awt.Component
- Apache Wicket 组件树,参见 Component 和 MarkupContainer
组合模式的优缺点
优势
- 简化了客户端代码,因为它可以以统一的方式处理组合结构和单个对象。
- 使添加新的组件类型变得更容易,因为现有代码不需要更改。
权衡
- 可能会使设计过于通用。可能难以限制组合的组件。
- 可能会使限制组合中组件类型的难度更大。