1. 为什么我们需要组合模式?
树形结构在软件开发中无处不在。从文件系统的目录结构,到企业组织架构图,再到电商平台的商品分类,我们几乎每天都要与这种数据结构打交道。作为一名Java开发者,你是否曾经为处理这些嵌套关系而头疼不已?
我清楚地记得第一次接手公司CMS系统时的场景。当时需要实现一个多级菜单功能,我的第一反应是写一堆if-else来处理不同层级的菜单项。结果代码越写越复杂,维护成本呈指数级增长。直到团队里的架构师向我介绍了组合模式,才真正解决了这个痛点。
组合模式(Composite Pattern)是GoF设计模式中结构型模式的一种,它允许你将对象组合成树形结构来表示"部分-整体"的层次关系。这种模式使得客户端可以统一处理单个对象和组合对象,无需关心它们之间的差异。
关键理解:组合模式不是用来创建树形结构的,而是用来处理已经存在的树形结构的。它的核心价值在于提供统一的操作接口。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 组合模式的三大核心要素
2.1 组件接口(Component)
这是整个模式的基础,定义了所有对象的通用接口。在Java中通常表现为抽象类或接口:
java复制public abstract class MenuComponent {
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
public String getName() {
throw new UnsupportedOperationException();
}
public String getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
}
这种设计被称为"安全组合模式",通过在父类中抛出异常来强制子类实现真正需要的方法。与之相对的"透明组合模式"则将所有方法声明为抽象方法。
2.2 叶子节点(Leaf)
叶子节点代表树形结构中的末端对象,不再包含子节点:
java复制public class MenuItem extends MenuComponent {
private String name;
private String description;
private boolean vegetarian;
private double price;
public MenuItem(String name, String description,
boolean vegetarian, double price) {
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
public boolean isVegetarian() {
return vegetarian;
}
public void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.println(", " + getPrice());
System.out.println(" -- " + getDescription());
}
}
2.3 组合类(Composite)
组合类包含子组件,实现了与子组件相关的操作:
java复制public class Menu extends MenuComponent {
private List<MenuComponent> menuComponents = new ArrayList<>();
private String name;
private String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
public MenuComponent getChild(int i) {
return menuComponents.get(i);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("---------------------");
for (MenuComponent component : menuComponents) {
component.print();
}
}
}
3. 组合模式的五种典型应用场景
3.1 图形界面组件系统
在Swing或JavaFX等GUI框架中,组合模式被广泛应用。例如:
java复制JFrame frame = new JFrame();
JPanel panel = new JPanel();
JButton button = new JButton("Click me");
panel.add(button);
frame.add(panel);
这种容器-组件结构正是组合模式的典型实现。Container相当于Composite,Component相当于Leaf。
3.2 文件系统表示
处理文件和目录结构时,组合模式能提供优雅的解决方案:
java复制public interface FileSystemComponent {
void display();
}
public class File implements FileSystemComponent {
private String name;
public File(String name) {
this.name = name;
}
public void display() {
System.out.println("File: " + name);
}
}
public class Directory implements FileSystemComponent {
private String name;
private List<FileSystemComponent> children = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
public void add(FileSystemComponent component) {
children.add(component);
}
public void display() {
System.out.println("Directory: " + name);
for (FileSystemComponent component : children) {
component.display();
}
}
}
3.3 组织架构管理
企业中的部门-员工关系天然适合用组合模式建模:
java复制public abstract class OrganizationComponent {
protected String name;
public OrganizationComponent(String name) {
this.name = name;
}
public abstract void display();
public abstract int getEmployeeCount();
}
public class Department extends OrganizationComponent {
private List<OrganizationComponent> children = new ArrayList<>();
public Department(String name) {
super(name);
}
public void add(OrganizationComponent component) {
children.add(component);
}
public void display() {
System.out.println("Department: " + name);
for (OrganizationComponent component : children) {
component.display();
}
}
public int getEmployeeCount() {
int count = 0;
for (OrganizationComponent component : children) {
count += component.getEmployeeCount();
}
return count;
}
}
public class Employee extends OrganizationComponent {
public Employee(String name) {
super(name);
}
public void display() {
System.out.println("Employee: " + name);
}
public int getEmployeeCount() {
return 1;
}
}
3.4 电商分类系统
电商平台的多级商品分类是组合模式的又一典型用例:
java复制public abstract class ProductCategory {
protected String name;
public ProductCategory(String name) {
this.name = name;
}
public abstract void display();
public abstract List<Product> getProducts();
}
public class Category extends ProductCategory {
private List<ProductCategory> subCategories = new ArrayList<>();
public Category(String name) {
super(name);
}
public void add(ProductCategory category) {
subCategories.add(category);
}
public void display() {
System.out.println("Category: " + name);
for (ProductCategory category : subCategories) {
category.display();
}
}
public List<Product> getProducts() {
List<Product> products = new ArrayList<>();
for (ProductCategory category : subCategories) {
products.addAll(category.getProducts());
}
return products;
}
}
public class Product extends ProductCategory {
private double price;
public Product(String name, double price) {
super(name);
this.price = price;
}
public void display() {
System.out.println("Product: " + name + " ($" + price + ")");
}
public List<Product> getProducts() {
return Collections.singletonList(this);
}
}
3.5 权限管理系统
RBAC(基于角色的访问控制)系统中,组合模式可以优雅地处理权限继承:
java复制public abstract class PermissionComponent {
public abstract boolean hasPermission(String permission);
}
public class PermissionLeaf extends PermissionComponent {
private String permission;
public PermissionLeaf(String permission) {
this.permission = permission;
}
public boolean hasPermission(String permission) {
return this.permission.equals(permission);
}
}
public class PermissionComposite extends PermissionComponent {
private List<PermissionComponent> children = new ArrayList<>();
public void add(PermissionComponent component) {
children.add(component);
}
public boolean hasPermission(String permission) {
for (PermissionComponent component : children) {
if (component.hasPermission(permission)) {
return true;
}
}
return false;
}
}
4. 组合模式的五个进阶技巧
4.1 缓存计算结果
对于频繁访问但计算成本高的操作(如计算总价),可以引入缓存机制:
java复制public abstract class ProductComponent {
protected boolean cacheValid = false;
protected double cachedPrice;
public abstract double getPrice();
public void invalidateCache() {
cacheValid = false;
// 如果有父组件,也需要通知父组件缓存失效
}
}
public class ProductComposite extends ProductComponent {
private List<ProductComponent> children = new ArrayList<>();
public double getPrice() {
if (!cacheValid) {
cachedPrice = 0;
for (ProductComponent component : children) {
cachedPrice += component.getPrice();
}
cacheValid = true;
}
return cachedPrice;
}
public void add(ProductComponent component) {
children.add(component);
invalidateCache();
}
}
4.2 实现迭代器模式
结合迭代器模式可以更灵活地遍历组合结构:
java复制public interface ComponentIterator extends Iterator<Component> {
// 可以添加特定于组合结构的遍历方法
}
public class CompositeIterator implements ComponentIterator {
private Stack<Iterator<Component>> stack = new Stack<>();
public CompositeIterator(Iterator<Component> iterator) {
stack.push(iterator);
}
public Component next() {
if (hasNext()) {
Iterator<Component> iterator = stack.peek();
Component component = iterator.next();
if (component instanceof Composite) {
stack.push(component.createIterator());
}
return component;
}
return null;
}
public boolean hasNext() {
if (stack.empty()) {
return false;
}
Iterator<Component> iterator = stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext();
}
return true;
}
}
4.3 支持访问者模式
通过访问者模式可以在不修改组件类的情况下添加新操作:
java复制public interface ComponentVisitor {
void visit(Leaf leaf);
void visit(Composite composite);
}
public class PriceVisitor implements ComponentVisitor {
private double totalPrice = 0;
public void visit(Leaf leaf) {
totalPrice += leaf.getPrice();
}
public void visit(Composite composite) {
// 组合节点本身可能没有价格
}
public double getTotalPrice() {
return totalPrice;
}
}
public abstract class Component {
public abstract void accept(ComponentVisitor visitor);
}
4.4 实现撤销操作
对于可变的组合结构,实现撤销操作需要考虑层次关系:
java复制public abstract class Component {
private Component parent;
public Component getParent() {
return parent;
}
protected void setParent(Component parent) {
this.parent = parent;
}
public void removeFromParent() {
if (parent != null) {
parent.remove(this);
}
}
}
public class Composite extends Component {
private List<Component> children = new ArrayList<>();
public void add(Component component) {
component.setParent(this);
children.add(component);
}
public void remove(Component component) {
component.setParent(null);
children.remove(component);
}
}
4.5 处理循环引用
在复杂结构中可能出现循环引用,需要特别处理:
java复制public class Composite extends Component {
private List<Component> children = new ArrayList<>();
public void add(Component component) {
if (isDescendant(component)) {
throw new IllegalArgumentException("循环引用检测");
}
component.setParent(this);
children.add(component);
}
private boolean isDescendant(Component component) {
if (this == component) {
return true;
}
for (Component child : children) {
if (child instanceof Composite) {
if (((Composite)child).isDescendant(component)) {
return true;
}
}
}
return false;
}
}
5. 组合模式在Spring框架中的应用
Spring框架中有多处使用了组合模式的思想,最典型的是:
5.1 BeanDefinition体系
Spring的BeanDefinition接口及其实现类形成了典型的组合结构:
java复制public interface BeanDefinition {
// 公共接口方法
}
public class RootBeanDefinition implements BeanDefinition {
// 根节点实现
}
public class ChildBeanDefinition implements BeanDefinition {
// 子节点实现
}
5.2 资源加载体系
Spring的资源抽象也采用了组合模式:
java复制public interface Resource extends InputStreamSource {
// 资源接口
}
public class FileSystemResource implements Resource {
// 文件系统资源实现
}
public class ClassPathResource implements Resource {
// 类路径资源实现
}
public class CompositeResource implements Resource {
private final Resource[] resources;
public CompositeResource(Resource... resources) {
this.resources = resources;
}
// 组合多个资源的行为
}
5.3 事务管理
Spring的事务属性定义也体现了组合思想:
java复制public interface TransactionDefinition {
int getPropagationBehavior();
int getIsolationLevel();
// 其他事务属性
}
public class DefaultTransactionDefinition implements TransactionDefinition {
// 默认实现
}
public class RuleBasedTransactionAttribute extends DefaultTransactionDefinition {
private List<RollbackRuleAttribute> rollbackRules;
// 添加特定规则
}
6. 组合模式的性能优化策略
6.1 延迟加载
对于大型树形结构,可以采用延迟加载策略:
java复制public class LazyComposite extends Component {
private List<Component> children;
private boolean loaded = false;
private void ensureLoaded() {
if (!loaded) {
// 从数据库或其他存储加载子节点
children = loadChildren();
loaded = true;
}
}
public void add(Component component) {
ensureLoaded();
children.add(component);
}
public Iterator<Component> iterator() {
ensureLoaded();
return children.iterator();
}
}
6.2 扁平化处理
对于频繁访问的深层结构,可以创建扁平化视图:
java复制public class FlattenedCompositeView {
private List<Component> allComponents = new ArrayList<>();
public FlattenedCompositeView(Composite root) {
flatten(root);
}
private void flatten(Component component) {
allComponents.add(component);
if (component instanceof Composite) {
for (Component child : (Composite)component) {
flatten(child);
}
}
}
public List<Component> getAllComponents() {
return Collections.unmodifiableList(allComponents);
}
}
6.3 批量操作优化
对于批量操作,可以优化为单次遍历:
java复制public class BatchOperationVisitor implements ComponentVisitor {
private List<Operation> operations = new ArrayList<>();
public void addOperation(Operation operation) {
operations.add(operation);
}
public void visit(Leaf leaf) {
for (Operation op : operations) {
op.execute(leaf);
}
}
public void visit(Composite composite) {
for (Operation op : operations) {
op.execute(composite);
}
}
}
6.4 并行处理
对于计算密集型操作,可以利用并行流:
java复制public class ParallelCompositeProcessor {
public static void process(Composite root) {
root.stream()
.parallel()
.forEach(component -> {
// 处理每个组件
});
}
}
6.5 增量更新
对于频繁更新的结构,可以采用增量更新策略:
java复制public class IncrementalComposite extends Composite {
private List<Component> addedComponents = new ArrayList<>();
private List<Component> removedComponents = new ArrayList<>();
@Override
public void add(Component component) {
super.add(component);
addedComponents.add(component);
}
@Override
public void remove(Component component) {
super.remove(component);
removedComponents.add(component);
}
public void commitChanges() {
// 将变更持久化
addedComponents.clear();
removedComponents.clear();
}
}
7. 组合模式与其它设计模式的联用
7.1 组合+装饰器模式
通过装饰器动态添加功能:
java复制public abstract class ComponentDecorator extends Component {
protected Component component;
public ComponentDecorator(Component component) {
this.component = component;
}
public void operation() {
component.operation();
}
}
public class LoggingDecorator extends ComponentDecorator {
public LoggingDecorator(Component component) {
super(component);
}
public void operation() {
System.out.println("Before operation");
super.operation();
System.out.println("After operation");
}
}
7.2 组合+策略模式
根据不同情况采用不同的遍历策略:
java复制public interface TraversalStrategy {
void traverse(Component component);
}
public class DepthFirstStrategy implements TraversalStrategy {
public void traverse(Component component) {
component.operation();
if (component instanceof Composite) {
for (Component child : (Composite)component) {
traverse(child);
}
}
}
}
public class BreadthFirstStrategy implements TraversalStrategy {
public void traverse(Component component) {
Queue<Component> queue = new LinkedList<>();
queue.add(component);
while (!queue.isEmpty()) {
Component current = queue.remove();
current.operation();
if (current instanceof Composite) {
queue.addAll(((Composite)current).getChildren());
}
}
}
}
7.3 组合+工厂模式
通过工厂创建组合结构:
java复制public interface ComponentFactory {
Component createLeaf();
Component createComposite();
}
public class DefaultComponentFactory implements ComponentFactory {
public Component createLeaf() {
return new Leaf();
}
public Component createComposite() {
return new Composite();
}
}
7.4 组合+观察者模式
实现组件变更通知:
java复制public abstract class ObservableComponent extends Component {
private List<ComponentListener> listeners = new ArrayList<>();
public void addListener(ComponentListener listener) {
listeners.add(listener);
}
public void removeListener(ComponentListener listener) {
listeners.remove(listener);
}
protected void fireChanged() {
for (ComponentListener listener : listeners) {
listener.componentChanged(this);
}
}
}
7.5 组合+状态模式
根据状态改变组件行为:
java复制public class StatefulComponent extends Component {
private ComponentState state;
public void setState(ComponentState state) {
this.state = state;
}
public void operation() {
state.handle(this);
}
}
public interface ComponentState {
void handle(StatefulComponent component);
}
8. 组合模式的测试策略
8.1 单元测试要点
测试组合模式时需要注意:
java复制public class CompositePatternTest {
@Test
public void testLeafOperation() {
Leaf leaf = new Leaf();
assertEquals("expected", leaf.operation());
}
@Test
public void testCompositeOperation() {
Composite composite = new Composite();
composite.add(new Leaf());
composite.add(new Leaf());
assertEquals("expected", composite.operation());
}
@Test
public void testNestedComposite() {
Composite root = new Composite();
Composite child = new Composite();
child.add(new Leaf());
root.add(child);
assertEquals("expected", root.operation());
}
}
8.2 性能测试要点
对于大型组合结构需要关注:
java复制public class CompositePerformanceTest {
@Test
public void testLargeStructureTraversal() {
Composite root = createLargeStructure(1000);
long start = System.currentTimeMillis();
root.traverse();
long duration = System.currentTimeMillis() - start;
assertTrue(duration < 1000);
}
}
8.3 内存使用测试
组合结构可能占用大量内存:
java复制public class CompositeMemoryTest {
@Test
public void testMemoryUsage() {
long before = Runtime.getRuntime().freeMemory();
Composite root = createLargeStructure(10000);
long after = Runtime.getRuntime().freeMemory();
assertTrue((before - after) < 10 * 1024 * 1024);
}
}
8.4 并发测试
验证线程安全性:
java复制public class CompositeConcurrencyTest {
@Test
public void testConcurrentModification() {
Composite root = new Composite();
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executor.execute(() -> {
root.add(new Leaf());
root.traverse();
});
}
executor.shutdown();
assertTrue(executor.awaitTermination(1, TimeUnit.MINUTES));
}
}
8.5 边界条件测试
特别注意边界情况:
java复制public class CompositeEdgeCaseTest {
@Test
public void testEmptyComposite() {
Composite composite = new Composite();
assertEquals(0, composite.size());
}
@Test(expected = IllegalArgumentException.class)
public void testNullChild() {
Composite composite = new Composite();
composite.add(null);
}
@Test
public void testSingleChild() {
Composite composite = new Composite();
composite.add(new Leaf());
assertEquals(1, composite.size());
}
}
9. 组合模式的常见误用与规避
9.1 过度设计陷阱
不是所有层次结构都需要组合模式。如果满足以下条件,可能不需要:
- 结构简单且不会变化
- 不需要统一处理叶子节点和组合节点
- 层次深度固定且很浅
9.2 违反单一职责原则
组合类不应承担过多与组合无关的责任。如果发现组合类变得臃肿,考虑:
- 将非核心功能移到装饰器中
- 使用策略模式封装可变行为
- 通过访问者模式添加新操作
9.3 忽略性能影响
深层嵌套的组合结构可能导致:
- 递归操作栈溢出
- 遍历性能低下
- 内存占用过高
解决方案:
- 限制最大深度
- 实现迭代器替代递归
- 考虑扁平化处理
9.4 不恰当的共享状态
在组合结构中共享状态要特别小心:
java复制// 反模式示例
public class BadComposite {
private static SharedState state; // 静态共享状态
public void operation() {
// 所有实例共享同一状态
}
}
正确做法:
java复制public class GoodComposite {
private final SharedState state;
public GoodComposite(SharedState state) {
this.state = state;
}
public void operation() {
// 明确的状态传递
}
}
9.5 忽略循环引用检测
没有循环引用检测可能导致:
java复制// 危险代码
Composite a = new Composite();
Composite b = new Composite();
a.add(b);
b.add(a); // 循环引用!
解决方案已在4.5节介绍,务必在实际应用中实现循环检测。
10. 组合模式在现代Java中的演进
10.1 使用Stream API处理组合结构
Java 8的Stream API为组合模式带来了新思路:
java复制public class StreamEnabledComposite extends Composite {
public Stream<Component> stream() {
return Stream.concat(
Stream.of(this),
getChildren().stream().flatMap(Component::stream)
);
}
}
// 使用示例
root.stream()
.filter(c -> c instanceof Leaf)
.forEach(Component::operation);
10.2 利用Records简化代码
Java 14引入的Records可以简化组件定义:
java复制public record LeafRecord(String name) implements Component {
public void operation() {
System.out.println("Leaf: " + name);
}
}
10.3 模式匹配简化类型检查
Java 16的模式匹配instanceof可以简化组件处理:
java复制public void traverse(Component component) {
if (component instanceof Leaf leaf) {
leaf.operation();
} else if (component instanceof Composite composite) {
for (Component child : composite.getChildren()) {
traverse(child);
}
}
}
10.4 使用Sealed Classes定义组件层次
Java 17的密封类可以更好地控制组件继承:
java复制public sealed interface Component
permits Leaf, Composite {
void operation();
}
public final class Leaf implements Component {
public void operation() { /* ... */ }
}
public final class Composite implements Component {
private List<Component> children;
public void operation() { /* ... */ }
}
10.5 虚拟线程优化遍历性能
Java 19的虚拟线程可以优化大型结构的并行处理:
java复制public class VirtualThreadTraverser {
public static void traverse(Component root) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
traverse(root, executor);
}
}
private static void traverse(Component component, ExecutorService executor) {
executor.submit(() -> {
component.operation();
if (component instanceof Composite composite) {
for (Component child : composite.getChildren()) {
traverse(child, executor);
}
}
});
}
}
11. 组合模式实战:实现一个动态配置系统
让我们通过一个完整的例子来巩固所学内容。假设我们需要实现一个动态配置系统,支持:
- 多级配置继承
- 类型安全的配置值获取
- 配置变更通知
11.1 基础结构设计
java复制public sealed interface ConfigNode
permits ConfigLeaf, ConfigComposite {
String getPath();
Optional<ConfigValue<?>> getValue(String key);
void addListener(ConfigListener listener);
}
public record ConfigValue<T>(String key, T value) {
// 类型安全的配置值
}
public interface ConfigListener {
void onConfigChanged(ConfigNode node);
}
11.2 叶子节点实现
java复制public final class ConfigLeaf implements ConfigNode {
private final String path;
private final Map<String, ConfigValue<?>> values;
private final List<ConfigListener> listeners = new CopyOnWriteArrayList<>();
public ConfigLeaf(String path, Map<String, ConfigValue<?>> values) {
this.path = path;
this.values = new ConcurrentHashMap<>(values);
}
public String getPath() {
return path;
}
public Optional<ConfigValue<?>> getValue(String key) {
return Optional.ofNullable(values.get(key));
}
public void updateValue(String key, ConfigValue<?> newValue) {
values.put(key, newValue);
listeners.forEach(l -> l.onConfigChanged(this));
}
public void addListener(ConfigListener listener) {
listeners.add(listener);
}
}
11.3 组合节点实现
java复制public final class ConfigComposite implements ConfigNode {
private final String path;
private final List<ConfigNode> children = new CopyOnWriteArrayList<>();
private final List<ConfigListener> listeners = new CopyOnWriteArrayList<>();
public ConfigComposite(String path) {
this.path = path;
}
public String getPath() {
return path;
}
public Optional<ConfigValue<?>> getValue(String key) {
for (ConfigNode child : children) {
Optional<ConfigValue<?>> value = child.getValue(key);
if (value.isPresent()) {
return value;
}
}
return Optional.empty();
}
public void addChild(ConfigNode child) {
children.add(child);
child.addListener(node -> listeners.forEach(l -> l.onConfigChanged(this)));
listeners.forEach(l -> l.onConfigChanged(this));
}
public void addListener(ConfigListener listener) {
listeners.add(listener);
}
}
11.4 使用示例
java复制public class ConfigSystem {
public static void main(String[] args) {
ConfigComposite root = new ConfigComposite("root");
ConfigLeaf dbConfig = new ConfigLeaf("db", Map.of(
"url", new ConfigValue<>("url", "jdbc:mysql://localhost:3306/mydb"),
"user", new ConfigValue<>("user", "admin")
));
ConfigLeaf cacheConfig = new ConfigLeaf("cache", Map.of(
"size", new ConfigValue<>("size", 1000),
"ttl", new ConfigValue<>("ttl", 3600)
));
root.addChild(dbConfig);
root.addChild(cacheConfig);
root.addListener(node -> {
System.out.println("Config changed at: " + node.getPath());
});
// 获取配置
Optional<ConfigValue<?>> url = root.getValue("url");
url.ifPresent(v -> System.out.println(v.key() + "=" + v.value()));
// 更新配置
dbConfig.updateValue("user", new ConfigValue<>("user", "newadmin"));
}
}
11.5 高级功能扩展
我们可以进一步扩展这个系统:
java复制// 类型安全的配置获取
public <T> Optional<T> getValueAs(String key, Class<T> type) {
return getValue(key)
.filter(v -> type.isInstance(v.value()))
.map(v -> type.cast(v.value()));
}
// 配置验证
public interface ConfigValidator {
boolean isValid(ConfigNode node);
}
public class ConfigValidationComposite implements ConfigNode {
private final ConfigNode delegate;
private final List<ConfigValidator> validators;
public void updateValue(String key, ConfigValue<?> newValue) {
if (validators.stream().allMatch(v -> v.isValid(this))) {
delegate.updateValue(key, newValue);
}
}
}
// 配置历史记录
public class ConfigHistoryDecorator implements ConfigNode {
private final ConfigNode delegate;
private final Deque<ConfigSnapshot> history = new ArrayDeque<>();
public void updateValue(String key, ConfigValue<?> newValue) {
history.push(new ConfigSnapshot(delegate));
delegate.updateValue(key, newValue);
}
public void rollback() {
if (!history.isEmpty()) {
history.pop().restore();
}
}
}
12. 组合模式面试精要
12.1 高频面试问题
-
组合模式与继承的区别是什么?
- 组合模式强调"has-a"关系,继承强调"is-a"关系
- 组合模式运行时可以动态改变结构,继承是静态的
- 组合模式可以形成任意复杂的结构,继承是严格的树形结构
-
组合模式与装饰器模式的异同?
- 相同点:都使用组合技术
- 不同点:
- 装饰器模式目的是增强功能
- 组合模式目的是表示层次结构
- 装饰器通常只有一个组件,组合模式有多个子组件
-
如何处理组合结构中的循环引用?
- 在添加子节点时检查祖先链(如4.5节所示)
- 使用弱引用打破强引用环
- 设计时避免出现循环引用的需求
-
组合模式在JDK中有哪些应用?
- java.awt.Component 和 Container
- javax.swing.JComponent
- java.util.Map 和 AbstractMap
- java.util.Collection 和 AbstractCollection
-
组合模式的优缺点分析
- 优点:
- 简化客户端代码
- 容易添加新组件类型
- 可以构建复杂的树形结构
- 缺点:
- 设计可能过度通用化
- 类型系统难以约束组件关系
- 深层次结构可能影响性能
- 优点:
12.2 实战编码题
题目: 实现一个支持undo操作的组合模式结构
java复制public abstract class UndoableComponent {
private UndoableComponent parent;
public abstract void operation();
public abstract UndoableComponent clone();
protected void setParent(UndoableComponent parent) {
this.parent = parent;
}
public UndoableComponent getParent() {
return parent;
}
public void saveState() {
// 保存当前状态到历史记录
HistoryManager.save(this.clone());
}
public static void undo() {
HistoryManager.restore();
}
}
public class UndoableComposite extends UndoableComponent {
private List<UndoableComponent> children = new ArrayList<>();
public void add(UndoableComponent component) {
component.setParent(this);
children.add(component);
saveState();
}
public void operation() {
System.out.println("Composite operation");
for (UndoableComponent child : children) {
child.operation();
}
}
public UndoableComponent clone() {
UndoableComposite copy = new UndoableComposite();
for (UndoableComponent child : children) {
copy.add(child.clone());
}
return copy;
}
}
12.3 系统设计题
题目: 如何设计一个支持百万级节点的组合结构?
解决方案:
-
内存优化
- 使用flyweight模式共享叶子节点状态
- 采用原始类型集合减少对象开销
- 实现懒加载,只加载访问的节点
-
遍历优化
- 实现并行遍历算法
- 提供基于游标的增量遍历
- 支持范围查询和过滤
-
持久化策略
- 使用关系型数据库存储,合理设计表结构
- 考虑使用图数据库如Neo4j
- 实现分片存储,按子树分区
-
缓存策略
- 为热点子树添加缓存
- 实现多级缓存(内存、分布式)
- 采用写时复制策略减少锁竞争
-
API设计
- 提供批量操作接口
- 支持异步操作和回调
- 实现基于事件的变更通知
12.4 设计模式组合题
题目: 如何结合组合模式和访问者模式实现配置检查?
解决方案:
java复制public interface ConfigVisitor {
void visit(ConfigLeaf leaf);
void visit(ConfigComposite composite);
}
public class ValidationVisitor implements ConfigVisitor {
private final List<ConfigError> errors = new ArrayList<>();
public void visit(ConfigLeaf leaf) {
if (leaf.getPath().length() > 100) {
errors.add(new ConfigError("Path too long", leaf));
}
}
public void visit(ConfigComposite composite) {
if (composite.getChildren().isEmpty()) {
errors.add(new ConfigError("Empty composite", composite));
}
}
public List<ConfigError> getErrors() {
return Collections.unmodifiableList(errors);
}
}
public class ConfigError {
private final String message;
private final ConfigNode node;
// constructor, getters
}
12.5 性能优化题
题目: 如何优化深度嵌套组合结构的性能?
优化方案:
- 扁平化索引
java复制public class FlattenedIndex { private final Map<String, ConfigNode> index = new HashMap<>(); public void buildIndex(ConfigNode root) { index.put(root.getPath(),
