组合模式 (Composite Pattern)
将对象组合成树形结构以表示"部分-整体"的层次结构
模式定义
组合模式是一种结构型设计模式,它将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
模式结构
graph TD
A[Client] --> B((Component))
B --> C[Leaf]
B --> D[Composite]
D --> E[Operation]
D --> F[Add/Remove]
实现方式
基础实现
定义组件接口,实现叶子节点和复合节点。
// 组件抽象类
public abstract class Employee {
protected String name;
protected String dept;
protected int salary;
public Employee(String name, String dept, int sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
}
public abstract void add(Employee e);
public abstract void remove(Employee e);
public abstract List getSubordinates();
public String toString() {
return ("Employee :[ Name : " + name + ", dept : " + dept + ", salary :" + salary + " ]");
}
}
// 叶子节点 - 普通员工
import java.util.ArrayList;
import java.util.List;
public class Developer extends Employee {
public Developer(String name, String dept, int salary) {
super(name, dept, salary);
}
@Override
public void add(Employee e) {
// 叶子节点不支持添加操作
throw new UnsupportedOperationException();
}
@Override
public void remove(Employee e) {
// 叶子节点不支持删除操作
throw new UnsupportedOperationException();
}
@Override
public List getSubordinates() {
// 叶子节点没有下属
return new ArrayList<>();
}
}
// 叶子节点 - 设计师
public class Designer extends Employee {
public Designer(String name, String dept, int salary) {
super(name, dept, salary);
}
@Override
public void add(Employee e) {
throw new UnsupportedOperationException();
}
@Override
public void remove(Employee e) {
throw new UnsupportedOperationException();
}
@Override
public List getSubordinates() {
return new ArrayList<>();
}
}
// 复合节点 - 管理者
import java.util.ArrayList;
import java.util.List;
public class Manager extends Employee {
private List subordinates;
public Manager(String name, String dept, int salary) {
super(name, dept, salary);
subordinates = new ArrayList<>();
}
@Override
public void add(Employee e) {
subordinates.add(e);
}
@Override
public void remove(Employee e) {
subordinates.remove(e);
}
@Override
public List getSubordinates() {
return subordinates;
}
}
使用示例
import java.util.List;
public class CompositePatternDemo {
public static void main(String[] args) {
// 创建员工
Employee CEO = new Manager("John", "CEO", 30000);
Employee headSales = new Manager("Robert", "Head Sales", 20000);
Employee headMarketing = new Manager("Michel", "Head Marketing", 20000);
Employee clerk1 = new Developer("Laura", "Marketing", 10000);
Employee clerk2 = new Developer("Bob", "Marketing", 10000);
Employee salesExecutive1 = new Developer("Richard", "Sales", 10000);
Employee salesExecutive2 = new Developer("Rob", "Sales", 10000);
// 创建组织结构
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
// 打印组织结构
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System.out.println(employee);
}
}
}
}
文件系统示例
使用组合模式实现简单的文件系统结构。
// 文件系统组件接口
public abstract class FileSystemComponent {
protected String name;
public FileSystemComponent(String name) {
this.name = name;
}
public abstract void display(int depth);
public abstract long getSize();
// 默认实现,叶子节点不支持这些操作
public void add(FileSystemComponent component) {
throw new UnsupportedOperationException("不支持添加操作");
}
public void remove(FileSystemComponent component) {
throw new UnsupportedOperationException("不支持删除操作");
}
public FileSystemComponent getChild(int i) {
throw new UnsupportedOperationException("不支持获取子节点操作");
}
}
// 叶子节点 - 文件
public class File extends FileSystemComponent {
private long size;
public File(String name, long size) {
super(name);
this.size = size;
}
@Override
public void display(int depth) {
System.out.println(" ".repeat(depth) + "- File: " + name + " (" + size + " bytes)");
}
@Override
public long getSize() {
return size;
}
}
// 复合节点 - 文件夹
import java.util.ArrayList;
import java.util.List;
public class Folder extends FileSystemComponent {
private List children = new ArrayList<>();
public Folder(String name) {
super(name);
}
@Override
public void add(FileSystemComponent component) {
children.add(component);
}
@Override
public void remove(FileSystemComponent component) {
children.remove(component);
}
@Override
public FileSystemComponent getChild(int i) {
return children.get(i);
}
@Override
public void display(int depth) {
System.out.println(" ".repeat(depth) + "+ Folder: " + name);
for (FileSystemComponent component : children) {
component.display(depth + 2);
}
}
@Override
public long getSize() {
long totalSize = 0;
for (FileSystemComponent component : children) {
totalSize += component.getSize();
}
return totalSize;
}
}
// 客户端使用
public class FileSystemDemo {
public static void main(String[] args) {
// 创建文件系统结构
Folder root = new Folder("root");
Folder documents = new Folder("Documents");
Folder pictures = new Folder("Pictures");
Folder music = new Folder("Music");
File doc1 = new File("resume.doc", 1024);
File doc2 = new File("reference.pdf", 2048);
File pic1 = new File("photo1.jpg", 204800);
File pic2 = new File("photo2.jpg", 307200);
File song1 = new File("song1.mp3", 5120000);
File song2 = new File("song2.mp3", 4096000);
// 构建目录结构
root.add(documents);
root.add(pictures);
root.add(music);
documents.add(doc1);
documents.add(doc2);
pictures.add(pic1);
pictures.add(pic2);
music.add(song1);
music.add(song2);
// 显示文件系统结构
root.display(0);
System.out.println("\n总大小: " + root.getSize() + " bytes");
}
}
经典案例
AWT/Swing GUI组件
Java AWT和Swing中的组件容器是组合模式的经典应用。
// Swing中的组合模式示例
import javax.swing.*;
import java.awt.*;
public class SwingCompositeExample {
public static void main(String[] args) {
JFrame frame = new JFrame("组合模式示例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
// JPanel是容器组件(Composite)
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
// JLabel是叶子组件(Leaf)
JLabel label = new JLabel("这是一个标签", JLabel.CENTER);
// JButton是叶子组件(Leaf)
JButton button1 = new JButton("按钮1");
JButton button2 = new JButton("按钮2");
// JPanel可以包含其他组件
panel.add(label, BorderLayout.CENTER);
panel.add(button1, BorderLayout.NORTH);
panel.add(button2, BorderLayout.SOUTH);
// JFrame也可以包含JPanel
frame.add(panel);
frame.setVisible(true);
}
}
适用场景
- 表示对象的部分-整体层次结构
- 希望用户忽略组合对象与单个对象的不同
- 对象结构是树形结构
- 当需要表示对象的"部分-整体"层次结构时,组合模式能让客户端一致地处理单个对象和组合对象
- 当希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象时
- 当对象结构是树形结构时,可以使用组合模式来表示树形结构,让客户端一致地处理树形结构
优缺点
优点
- 高层模块调用简单:客户端可以一致地使用组合结构和单个对象
- 节点自由增加:容易在组合体中加入新的构件
- 定义了包含基本对象和组合对象的类层次结构:基本对象可以被组合成更复杂的组合对象
- 简化客户端代码:客户端可以一致地使用组合对象和单个对象
- 更容易在组合体内加入新构件:客户端不会因为加入了新的构件而更改代码
缺点
- 很难限制容器中的构件类型
- 使设计变得更加抽象,不容易理解
- 不容易限制容器中的构件类型:有时候系统需要依赖于构件的类型
- 用继承的方法来增加新的行为很困难:继承会使系统中类的数量大量增加