设计模式-组合模式详解

组合模式详解

目录

  1. 组合模式简介
  2. 核心流程
  3. 重难点分析
  4. Spring中的源码分析
  5. 具体使用场景
  6. 面试高频点

组合模式简介

定义

组合模式(Composite Pattern)是一种结构型设计模式,它将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

核心思想

  • 统一接口:叶子节点和组合节点使用相同的接口
  • 递归结构:支持树形结构的递归操作
  • 透明性:客户端无需区分叶子节点和组合节点
  • 整体-部分:将部分对象组合成整体对象

模式结构

  • Component(抽象构件):为组合中的对象声明接口,在适当情况下实现所有类共有接口的默认行为
  • Leaf(叶子构件):在组合中表示叶子节点对象,叶子节点没有子节点
  • Composite(组合构件):定义有子部件的那些部件的行为,存储子部件,在Component接口中实现与子部件有关的操作

核心流程

组合模式流程图

操作流程
组合结构
统一接口调用
递归处理
结果聚合
根节点Composite
子节点Composite
叶子节点Leaf
叶子节点Leaf
客户端
创建组合结构
添加叶子节点
添加组合节点
调用叶子操作
调用组合操作
遍历子节点
递归调用子节点操作
返回结果

基本实现流程

1. 定义抽象构件
// 抽象构件
public abstract class Component {
    protected String name;
  
    public Component(String name) {
        this.name = name;
    }
  
    // 公共操作
    public abstract void operation();
  
    // 组合相关操作(默认实现为空)
    public void add(Component component) {
        throw new UnsupportedOperationException("不支持添加操作");
    }
  
    public void remove(Component component) {
        throw new UnsupportedOperationException("不支持删除操作");
    }
  
    public Component getChild(int index) {
        throw new UnsupportedOperationException("不支持获取子节点操作");
    }
  
    public String getName() {
        return name;
    }
}
2. 实现叶子构件
// 叶子构件
public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }
  
    @Override
    public void operation() {
        System.out.println("叶子节点 " + name + " 执行操作");
    }
}
3. 实现组合构件
// 组合构件
public class Composite extends Component {
    private List<Component> children = new ArrayList<>();
  
    public Composite(String name) {
        super(name);
    }
  
    @Override
    public void operation() {
        System.out.println("组合节点 " + name + " 执行操作");
        // 递归调用子节点的操作
        for (Component child : children) {
            child.operation();
        }
    }
  
    @Override
    public void add(Component component) {
        children.add(component);
    }
  
    @Override
    public void remove(Component component) {
        children.remove(component);
    }
  
    @Override
    public Component getChild(int index) {
        if (index >= 0 && index < children.size()) {
            return children.get(index);
        }
        return null;
    }
  
    public List<Component> getChildren() {
        return new ArrayList<>(children);
    }
}
4. 客户端使用
public class Client {
    public static void main(String[] args) {
        // 创建根节点
        Composite root = new Composite("根目录");
      
        // 创建子节点
        Composite folder1 = new Composite("文件夹1");
        Composite folder2 = new Composite("文件夹2");
      
        // 创建叶子节点
        Leaf file1 = new Leaf("文件1.txt");
        Leaf file2 = new Leaf("文件2.txt");
        Leaf file3 = new Leaf("文件3.txt");
      
        // 构建树形结构
        root.add(folder1);
        root.add(folder2);
        root.add(file1);
      
        folder1.add(file2);
        folder2.add(file3);
      
        // 执行操作
        root.operation();
    }
}

重难点分析

重难点1:透明式组合模式 vs 安全式组合模式

问题描述

如何设计组合模式的接口,平衡透明性和安全性。

解决方案
// 透明式组合模式(推荐)
public abstract class Component {
    protected String name;
  
    public Component(String name) {
        this.name = name;
    }
  
    public abstract void operation();
  
    // 组合相关操作,叶子节点抛出异常
    public void add(Component component) {
        throw new UnsupportedOperationException("叶子节点不支持添加操作");
    }
  
    public void remove(Component component) {
        throw new UnsupportedOperationException("叶子节点不支持删除操作");
    }
  
    public Component getChild(int index) {
        throw new UnsupportedOperationException("叶子节点不支持获取子节点操作");
    }
}

// 安全式组合模式
public abstract class Component {
    protected String name;
  
    public Component(String name) {
        this.name = name;
    }
  
    public abstract void operation();
    // 不包含组合相关操作
}

public abstract class Composite extends Component {
    protected List<Component> children = new ArrayList<>();
  
    public Composite(String name) {
        super(name);
    }
  
    public abstract void add(Component component);
    public abstract void remove(Component component);
    public abstract Component getChild(int index);
}

// 使用建议:优先使用透明式,客户端代码更简洁

重难点2:递归操作的性能优化

问题描述

在深层嵌套的组合结构中,递归操作可能导致性能问题。

解决方案
// 1. 使用迭代代替递归
public class Composite extends Component {
    private List<Component> children = new ArrayList<>();
  
    @Override
    public void operation() {
        System.out.println("组合节点 " + name + " 执行操作");
      
        // 使用栈进行迭代遍历
        Stack<Component> stack = new Stack<>();
        for (Component child : children) {
            stack.push(child);
        }
      
        while (!stack.isEmpty()) {
            Component component = stack.pop();
            component.operation();
          
            if (component instanceof Composite) {
                Composite composite = (Composite) component;
                for (Component child : composite.getChildren()) {
                    stack.push(child);
                }
            }
        }
    }
}

// 2. 缓存计算结果
public abstract class Component {
    protected String name;
    protected boolean calculated = false;
    protected Object cachedResult;
  
    public Component(String name) {
        this.name = name;
    }
  
    public Object getResult() {
        if (!calculated) {
            cachedResult = calculate();
            calculated = true;
        }
        return cachedResult;
    }
  
    protected abstract Object calculate();
  
    public void invalidateCache() {
        calculated = false;
        cachedResult = null;
    }
}

// 3. 使用访问者模式优化遍历
public interface Visitor {
    void visit(Leaf leaf);
    void visit(Composite composite);
}

public class OperationVisitor implements Visitor {
    @Override
    public void visit(Leaf leaf) {
        System.out.println("访问叶子节点: " + leaf.getName());
    }
  
    @Override
    public void visit(Composite composite) {
        System.out.println("访问组合节点: " + composite.getName());
        for (Component child : composite.getChildren()) {
            child.accept(this);
        }
    }
}

public abstract class Component {
    public abstract void accept(Visitor visitor);
}

重难点3:组合结构的动态管理

问题描述

如何在运行时动态添加、删除、修改组合结构。

解决方案
// 1. 支持动态结构的组合模式
public class DynamicComposite extends Component {
    private List<Component> children = new ArrayList<>();
    private Map<String, Component> childMap = new HashMap<>();
  
    public DynamicComposite(String name) {
        super(name);
    }
  
    @Override
    public void add(Component component) {
        children.add(component);
        childMap.put(component.getName(), component);
        // 通知观察者
        notifyObservers("ADD", component);
    }
  
    @Override
    public void remove(Component component) {
        children.remove(component);
        childMap.remove(component.getName());
        // 通知观察者
        notifyObservers("REMOVE", component);
    }
  
    public Component findByName(String name) {
        return childMap.get(name);
    }
  
    public void move(Component component, Component newParent) {
        if (component instanceof DynamicComposite) {
            DynamicComposite oldParent = findParent(component);
            if (oldParent != null) {
                oldParent.remove(component);
            }
            ((DynamicComposite) newParent).add(component);
        }
    }
  
    private DynamicComposite findParent(Component component) {
        for (Component child : children) {
            if (child == component) {
                return this;
            }
            if (child instanceof DynamicComposite) {
                DynamicComposite result = ((DynamicComposite) child).findParent(component);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }
  
    // 观察者模式支持
    private List<CompositeObserver> observers = new ArrayList<>();
  
    public void addObserver(CompositeObserver observer) {
        observers.add(observer);
    }
  
    private void notifyObservers(String action, Component component) {
        for (CompositeObserver observer : observers) {
            observer.onComponentChanged(action, component);
        }
    }
}

// 2. 支持撤销操作的组合模式
public class UndoableComposite extends Component {
    private List<Component> children = new ArrayList<>();
    private Stack<CompositeCommand> commandHistory = new Stack<>();
  
    public void addWithUndo(Component component) {
        AddCommand command = new AddCommand(this, component);
        command.execute();
        commandHistory.push(command);
    }
  
    public void removeWithUndo(Component component) {
        RemoveCommand command = new RemoveCommand(this, component);
        command.execute();
        commandHistory.push(command);
    }
  
    public void undo() {
        if (!commandHistory.isEmpty()) {
            CompositeCommand command = commandHistory.pop();
            command.undo();
        }
    }
}

// 命令接口
public interface CompositeCommand {
    void execute();
    void undo();
}

// 添加命令
public class AddCommand implements CompositeCommand {
    private UndoableComposite parent;
    private Component child;
  
    public AddCommand(UndoableComposite parent, Component child) {
        this.parent = parent;
        this.child = child;
    }
  
    @Override
    public void execute() {
        parent.add(child);
    }
  
    @Override
    public void undo() {
        parent.remove(child);
    }
}

重难点4:组合模式的内存管理

问题描述

在复杂的组合结构中,如何避免内存泄漏和循环引用。

解决方案
// 1. 使用弱引用避免循环引用
public class WeakComposite extends Component {
    private List<WeakReference<Component>> children = new ArrayList<>();
    private Component parent;
  
    public void add(Component component) {
        children.add(new WeakReference<>(component));
        if (component instanceof WeakComposite) {
            ((WeakComposite) component).parent = this;
        }
    }
  
    @Override
    public void operation() {
        System.out.println("组合节点 " + name + " 执行操作");
      
        // 清理失效的弱引用
        children.removeIf(ref -> ref.get() == null);
      
        for (WeakReference<Component> ref : children) {
            Component child = ref.get();
            if (child != null) {
                child.operation();
            }
        }
    }
}

// 2. 实现自动清理机制
public class AutoCleanupComposite extends Component {
    private List<Component> children = new ArrayList<>();
    private boolean disposed = false;
  
    @Override
    public void operation() {
        if (disposed) {
            throw new IllegalStateException("组件已被销毁");
        }
      
        System.out.println("组合节点 " + name + " 执行操作");
        for (Component child : children) {
            child.operation();
        }
    }
  
    public void dispose() {
        if (!disposed) {
            // 递归销毁子组件
            for (Component child : children) {
                if (child instanceof AutoCleanupComposite) {
                    ((AutoCleanupComposite) child).dispose();
                }
            }
            children.clear();
            disposed = true;
        }
    }
  
    @Override
    protected void finalize() throws Throwable {
        try {
            dispose();
        } finally {
            super.finalize();
        }
    }
}

// 3. 使用对象池管理组件
public class ComponentPool {
    private final Queue<Component> pool = new ConcurrentLinkedQueue<>();
    private final int maxSize;
  
    public ComponentPool(int maxSize) {
        this.maxSize = maxSize;
    }
  
    public Component acquire() {
        Component component = pool.poll();
        if (component == null) {
            component = new Leaf("PooledComponent");
        }
        return component;
    }
  
    public void release(Component component) {
        if (pool.size() < maxSize) {
            // 重置组件状态
            component.reset();
            pool.offer(component);
        }
    }
}

Spring中的源码分析

Spring Bean的层次结构

// BeanDefinition接口作为抽象构件
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
  
    void setParentName(@Nullable String parentName);
    @Nullable
    String getParentName();
  
    void setBeanClassName(@Nullable String beanClassName);
    @Nullable
    String getBeanClassName();
  
    void setScope(@Nullable String scope);
    @Nullable
    String getScope();
  
    void setLazyInit(boolean lazyInit);
    boolean isLazyInit();
  
    void setDependsOn(@Nullable String... dependsOn);
    @Nullable
    String[] getDependsOn();
  
    void setFactoryBeanName(@Nullable String factoryBeanName);
    @Nullable
    String getFactoryBeanName();
  
    void setFactoryMethodName(@Nullable String factoryMethodName);
    @Nullable
    String getFactoryMethodName();
}

// 具体实现
public class GenericBeanDefinition extends AbstractBeanDefinition {
    @Nullable
    private String parentName;
  
    public GenericBeanDefinition() {
        super();
    }
  
    public GenericBeanDefinition(BeanDefinition original) {
        super(original);
    }
  
    @Override
    public void setParentName(@Nullable String parentName) {
        this.parentName = parentName;
    }
  
    @Override
    @Nullable
    public String getParentName() {
        return this.parentName;
    }
  
    @Override
    public AbstractBeanDefinition cloneBeanDefinition() {
        return new GenericBeanDefinition(this);
    }
}

Spring Security的权限层次结构

// ConfigAttribute接口
public interface ConfigAttribute extends Serializable {
    String getAttribute();
}

// 具体实现
public class SecurityConfig implements ConfigAttribute {
    private final String config;
  
    public SecurityConfig(String config) {
        Assert.hasText(config, "You must provide a configuration attribute");
        this.config = config;
    }
  
    @Override
    public String getAttribute() {
        return this.config;
    }
  
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof SecurityConfig) {
            SecurityConfig that = (SecurityConfig) obj;
            return this.config.equals(that.config);
        }
        return false;
    }
  
    @Override
    public int hashCode() {
        return this.config.hashCode();
    }
  
    @Override
    public String toString() {
        return this.config;
    }
}

// 权限组合
public class SecurityConfigCollection implements ConfigAttribute {
    private final Collection<ConfigAttribute> configAttributes;
  
    public SecurityConfigCollection(Collection<ConfigAttribute> configAttributes) {
        this.configAttributes = configAttributes;
    }
  
    @Override
    public String getAttribute() {
        return configAttributes.stream()
                .map(ConfigAttribute::getAttribute)
                .collect(Collectors.joining(", "));
    }
  
    public Collection<ConfigAttribute> getConfigAttributes() {
        return configAttributes;
    }
}

Spring MVC的HandlerMapping层次结构

// HandlerMapping接口
public interface HandlerMapping {
    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
    String HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".handler";
  
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

// 抽象实现
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
    private final Map<String, Object> urlMap = new LinkedHashMap<>();
    private final Map<PathPattern, Object> pathPatternMap = new LinkedHashMap<>();
  
    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
      
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }
      
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
      
        if (logger.isTraceEnabled()) {
            logger.trace("Mapped to " + handler);
        } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.REQUEST)) {
            logger.debug("Mapped to " + handler);
        }
      
        return executionChain;
    }
  
    @Nullable
    protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
}

Spring AOP的切面层次结构

// Pointcut接口
public interface Pointcut {
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
  
    Pointcut TRUE = TruePointcut.INSTANCE;
}

// 具体实现
public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
    private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
  
    static {
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
    }
  
    private String expression;
    private PointcutExpression pointcutExpression;
    private ClassFilter classFilter;
    private MethodMatcher methodMatcher;
  
    public void setExpression(@Nullable String expression) {
        this.expression = expression;
    }
  
    @Override
    public ClassFilter getClassFilter() {
        return this.classFilter;
    }
  
    @Override
    public MethodMatcher getMethodMatcher() {
        return this.methodMatcher;
    }
  
    @Override
    public boolean matches(Class<?> clazz) {
        return this.classFilter.matches(clazz);
    }
  
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return this.methodMatcher.matches(method, targetClass);
    }
}

具体使用场景

1. 文件系统

// 文件系统组件
public abstract class FileSystemComponent {
    protected String name;
    protected long size;
  
    public FileSystemComponent(String name) {
        this.name = name;
    }
  
    public abstract void display();
    public abstract long getSize();
  
    public String getName() {
        return name;
    }
}

// 文件(叶子节点)
public class File extends FileSystemComponent {
    private String content;
  
    public File(String name, String content) {
        super(name);
        this.content = content;
        this.size = content.length();
    }
  
    @Override
    public void display() {
        System.out.println("文件: " + name + " (大小: " + size + " 字节)");
    }
  
    @Override
    public long getSize() {
        return size;
    }
  
    public String getContent() {
        return content;
    }
}

// 文件夹(组合节点)
public class Directory extends FileSystemComponent {
    private List<FileSystemComponent> children = new ArrayList<>();
  
    public Directory(String name) {
        super(name);
    }
  
    @Override
    public void display() {
        System.out.println("文件夹: " + name);
        for (FileSystemComponent child : children) {
            child.display();
        }
    }
  
    @Override
    public long getSize() {
        long totalSize = 0;
        for (FileSystemComponent child : children) {
            totalSize += child.getSize();
        }
        return totalSize;
    }
  
    public void add(FileSystemComponent component) {
        children.add(component);
    }
  
    public void remove(FileSystemComponent component) {
        children.remove(component);
    }
  
    public List<FileSystemComponent> getChildren() {
        return new ArrayList<>(children);
    }
}

// 使用示例
public class FileSystemDemo {
    public static void main(String[] args) {
        // 创建根目录
        Directory root = new Directory("根目录");
      
        // 创建子目录
        Directory documents = new Directory("文档");
        Directory pictures = new Directory("图片");
      
        // 创建文件
        File file1 = new File("readme.txt", "这是一个说明文件");
        File file2 = new File("config.xml", "<config></config>");
        File file3 = new File("photo.jpg", "图片内容");
      
        // 构建文件系统
        root.add(documents);
        root.add(pictures);
        root.add(file1);
      
        documents.add(file2);
        pictures.add(file3);
      
        // 显示文件系统
        root.display();
        System.out.println("总大小: " + root.getSize() + " 字节");
    }
}

2. 组织架构管理

// 员工组件
public abstract class Employee {
    protected String name;
    protected String position;
    protected double salary;
  
    public Employee(String name, String position, double salary) {
        this.name = name;
        this.position = position;
        this.salary = salary;
    }
  
    public abstract void showDetails();
    public abstract double calculateTotalSalary();
    public abstract void add(Employee employee);
    public abstract void remove(Employee employee);
  
    public String getName() {
        return name;
    }
}

// 普通员工(叶子节点)
public class IndividualEmployee extends Employee {
    public IndividualEmployee(String name, String position, double salary) {
        super(name, position, salary);
    }
  
    @Override
    public void showDetails() {
        System.out.println("员工: " + name + ", 职位: " + position + ", 薪资: " + salary);
    }
  
    @Override
    public double calculateTotalSalary() {
        return salary;
    }
  
    @Override
    public void add(Employee employee) {
        throw new UnsupportedOperationException("普通员工不能添加下属");
    }
  
    @Override
    public void remove(Employee employee) {
        throw new UnsupportedOperationException("普通员工不能删除下属");
    }
}

// 管理者(组合节点)
public class Manager extends Employee {
    private List<Employee> subordinates = new ArrayList<>();
  
    public Manager(String name, String position, double salary) {
        super(name, position, salary);
    }
  
    @Override
    public void showDetails() {
        System.out.println("管理者: " + name + ", 职位: " + position + ", 薪资: " + salary);
        System.out.println("下属员工:");
        for (Employee subordinate : subordinates) {
            System.out.print("  ");
            subordinate.showDetails();
        }
    }
  
    @Override
    public double calculateTotalSalary() {
        double total = salary;
        for (Employee subordinate : subordinates) {
            total += subordinate.calculateTotalSalary();
        }
        return total;
    }
  
    @Override
    public void add(Employee employee) {
        subordinates.add(employee);
    }
  
    @Override
    public void remove(Employee employee) {
        subordinates.remove(employee);
    }
  
    public List<Employee> getSubordinates() {
        return new ArrayList<>(subordinates);
    }
}

// 使用示例
public class OrganizationDemo {
    public static void main(String[] args) {
        // 创建CEO
        Manager ceo = new Manager("张三", "CEO", 50000);
      
        // 创建部门经理
        Manager techManager = new Manager("李四", "技术经理", 30000);
        Manager salesManager = new Manager("王五", "销售经理", 28000);
      
        // 创建普通员工
        IndividualEmployee dev1 = new IndividualEmployee("赵六", "开发工程师", 15000);
        IndividualEmployee dev2 = new IndividualEmployee("钱七", "测试工程师", 12000);
        IndividualEmployee sales1 = new IndividualEmployee("孙八", "销售代表", 10000);
      
        // 构建组织架构
        ceo.add(techManager);
        ceo.add(salesManager);
      
        techManager.add(dev1);
        techManager.add(dev2);
        salesManager.add(sales1);
      
        // 显示组织架构
        ceo.showDetails();
        System.out.println("总薪资支出: " + ceo.calculateTotalSalary());
    }
}

3. 菜单系统

// 菜单组件
public abstract class MenuComponent {
    protected String name;
    protected String description;
    protected double price;
    protected boolean vegetarian;
  
    public MenuComponent(String name, String description) {
        this.name = name;
        this.description = description;
    }
  
    public abstract void print();
    public abstract void add(MenuComponent component);
    public abstract void remove(MenuComponent component);
    public abstract MenuComponent getChild(int index);
  
    public String getName() {
        return name;
    }
  
    public String getDescription() {
        return description;
    }
  
    public double getPrice() {
        return price;
    }
  
    public boolean isVegetarian() {
        return vegetarian;
    }
}

// 菜单项(叶子节点)
public class MenuItem extends MenuComponent {
    public MenuItem(String name, String description, double price, boolean vegetarian) {
        super(name, description);
        this.price = price;
        this.vegetarian = vegetarian;
    }
  
    @Override
    public void print() {
        System.out.print("  " + name);
        if (vegetarian) {
            System.out.print("(V)");
        }
        System.out.println(", " + price);
        System.out.println("     -- " + description);
    }
  
    @Override
    public void add(MenuComponent component) {
        throw new UnsupportedOperationException("菜单项不能添加子项");
    }
  
    @Override
    public void remove(MenuComponent component) {
        throw new UnsupportedOperationException("菜单项不能删除子项");
    }
  
    @Override
    public MenuComponent getChild(int index) {
        throw new UnsupportedOperationException("菜单项没有子项");
    }
}

// 菜单(组合节点)
public class Menu extends MenuComponent {
    private List<MenuComponent> menuComponents = new ArrayList<>();
  
    public Menu(String name, String description) {
        super(name, description);
    }
  
    @Override
    public void print() {
        System.out.println("\n" + name + ", " + description);
        System.out.println("---------------------");
      
        for (MenuComponent component : menuComponents) {
            component.print();
        }
    }
  
    @Override
    public void add(MenuComponent component) {
        menuComponents.add(component);
    }
  
    @Override
    public void remove(MenuComponent component) {
        menuComponents.remove(component);
    }
  
    @Override
    public MenuComponent getChild(int index) {
        if (index >= 0 && index < menuComponents.size()) {
            return menuComponents.get(index);
        }
        return null;
    }
}

// 使用示例
public class MenuDemo {
    public static void main(String[] args) {
        // 创建主菜单
        Menu pancakeHouseMenu = new Menu("煎饼屋菜单", "早餐菜单");
        Menu dinerMenu = new Menu("餐厅菜单", "午餐菜单");
        Menu dessertMenu = new Menu("甜点菜单", "甜点菜单");
      
        // 添加菜单项
        pancakeHouseMenu.add(new MenuItem("煎饼", "薄煎饼配鸡蛋", 2.99, true));
        pancakeHouseMenu.add(new MenuItem("华夫饼", "华夫饼配蓝莓", 3.59, true));
      
        dinerMenu.add(new MenuItem("素食汉堡", "素食汉堡配薯条", 2.99, true));
        dinerMenu.add(new MenuItem("培根汉堡", "培根汉堡配薯条", 3.49, false));
      
        dessertMenu.add(new MenuItem("苹果派", "苹果派配冰淇淋", 1.59, true));
        dessertMenu.add(new MenuItem("芝士蛋糕", "纽约芝士蛋糕", 1.99, true));
      
        // 将甜点菜单添加到餐厅菜单
        dinerMenu.add(dessertMenu);
      
        // 显示菜单
        pancakeHouseMenu.print();
        dinerMenu.print();
    }
}

4. 图形绘制系统

// 图形组件
public abstract class Graphic {
    protected String name;
    protected int x, y;
  
    public Graphic(String name, int x, int y) {
        this.name = name;
        this.x = x;
        this.y = y;
    }
  
    public abstract void draw();
    public abstract void move(int deltaX, int deltaY);
    public abstract void add(Graphic graphic);
    public abstract void remove(Graphic graphic);
    public abstract Graphic getChild(int index);
  
    public String getName() {
        return name;
    }
}

// 简单图形(叶子节点)
public class Circle extends Graphic {
    private int radius;
  
    public Circle(String name, int x, int y, int radius) {
        super(name, x, y);
        this.radius = radius;
    }
  
    @Override
    public void draw() {
        System.out.println("绘制圆形: " + name + " at (" + x + "," + y + ") radius=" + radius);
    }
  
    @Override
    public void move(int deltaX, int deltaY) {
        this.x += deltaX;
        this.y += deltaY;
    }
  
    @Override
    public void add(Graphic graphic) {
        throw new UnsupportedOperationException("简单图形不能添加子图形");
    }
  
    @Override
    public void remove(Graphic graphic) {
        throw new UnsupportedOperationException("简单图形不能删除子图形");
    }
  
    @Override
    public Graphic getChild(int index) {
        throw new UnsupportedOperationException("简单图形没有子图形");
    }
}

public class Rectangle extends Graphic {
    private int width, height;
  
    public Rectangle(String name, int x, int y, int width, int height) {
        super(name, x, y);
        this.width = width;
        this.height = height;
    }
  
    @Override
    public void draw() {
        System.out.println("绘制矩形: " + name + " at (" + x + "," + y + ") size=" + width + "x" + height);
    }
  
    @Override
    public void move(int deltaX, int deltaY) {
        this.x += deltaX;
        this.y += deltaY;
    }
  
    @Override
    public void add(Graphic graphic) {
        throw new UnsupportedOperationException("简单图形不能添加子图形");
    }
  
    @Override
    public void remove(Graphic graphic) {
        throw new UnsupportedOperationException("简单图形不能删除子图形");
    }
  
    @Override
    public Graphic getChild(int index) {
        throw new UnsupportedOperationException("简单图形没有子图形");
    }
}

// 复合图形(组合节点)
public class CompositeGraphic extends Graphic {
    private List<Graphic> children = new ArrayList<>();
  
    public CompositeGraphic(String name, int x, int y) {
        super(name, x, y);
    }
  
    @Override
    public void draw() {
        System.out.println("绘制复合图形: " + name + " at (" + x + "," + y + ")");
        for (Graphic child : children) {
            child.draw();
        }
    }
  
    @Override
    public void move(int deltaX, int deltaY) {
        this.x += deltaX;
        this.y += deltaY;
        // 移动所有子图形
        for (Graphic child : children) {
            child.move(deltaX, deltaY);
        }
    }
  
    @Override
    public void add(Graphic graphic) {
        children.add(graphic);
    }
  
    @Override
    public void remove(Graphic graphic) {
        children.remove(graphic);
    }
  
    @Override
    public Graphic getChild(int index) {
        if (index >= 0 && index < children.size()) {
            return children.get(index);
        }
        return null;
    }
  
    public List<Graphic> getChildren() {
        return new ArrayList<>(children);
    }
}

// 使用示例
public class GraphicsDemo {
    public static void main(String[] args) {
        // 创建复合图形
        CompositeGraphic picture = new CompositeGraphic("图片", 0, 0);
      
        // 创建简单图形
        Circle circle = new Circle("圆形", 10, 10, 5);
        Rectangle rectangle = new Rectangle("矩形", 20, 20, 10, 8);
      
        // 创建子复合图形
        CompositeGraphic group = new CompositeGraphic("组", 30, 30);
        Circle circle2 = new Circle("圆形2", 0, 0, 3);
        Rectangle rectangle2 = new Rectangle("矩形2", 5, 5, 6, 4);
      
        // 构建图形层次结构
        picture.add(circle);
        picture.add(rectangle);
        picture.add(group);
      
        group.add(circle2);
        group.add(rectangle2);
      
        // 绘制图形
        picture.draw();
      
        // 移动图形
        System.out.println("\n移动图形:");
        picture.move(5, 5);
        picture.draw();
    }
}

面试高频点

面试知识点思维导图

组合模式面试点
基本概念
实现方式
重难点
Spring应用
设计原则
实际应用
部分-整体层次结构
统一接口
递归结构
透明性
Component抽象构件
Leaf叶子构件
Composite组合构件
客户端调用
透明式vs安全式
递归操作性能
动态管理
内存管理
BeanDefinition层次
Security权限层次
HandlerMapping层次
AOP切面层次
开闭原则
单一职责
里氏替换
组合优于继承
文件系统
组织架构
菜单系统
图形绘制

1. 组合模式的基本概念

问题:什么是组合模式?

答案要点:

  • 将对象组合成树形结构以表示"部分-整体"的层次结构
  • 使得用户对单个对象和组合对象的使用具有一致性
  • 属于结构型设计模式
  • 解决树形结构的递归操作问题
问题:组合模式有哪些角色?

答案要点:

  • Component(抽象构件):为组合中的对象声明接口
  • Leaf(叶子构件):在组合中表示叶子节点对象
  • Composite(组合构件):定义有子部件的那些部件的行为
  • Client(客户端):通过Component接口操作组合对象

2. 实现方式相关

问题:如何实现组合模式?

答案要点:

// 1. 定义抽象构件
public abstract class Component {
    protected String name;
  
    public Component(String name) {
        this.name = name;
    }
  
    public abstract void operation();
  
    public void add(Component component) {
        throw new UnsupportedOperationException("不支持添加操作");
    }
  
    public void remove(Component component) {
        throw new UnsupportedOperationException("不支持删除操作");
    }
}

// 2. 实现叶子构件
public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }
  
    @Override
    public void operation() {
        System.out.println("叶子节点 " + name + " 执行操作");
    }
}

// 3. 实现组合构件
public class Composite extends Component {
    private List<Component> children = new ArrayList<>();
  
    public Composite(String name) {
        super(name);
    }
  
    @Override
    public void operation() {
        System.out.println("组合节点 " + name + " 执行操作");
        for (Component child : children) {
            child.operation();
        }
    }
  
    @Override
    public void add(Component component) {
        children.add(component);
    }
  
    @Override
    public void remove(Component component) {
        children.remove(component);
    }
}

3. 重难点问题

问题:透明式组合模式和安全式组合模式的区别?

答案要点:

  • 透明式:在Component中定义所有方法,叶子节点抛出异常
  • 安全式:只在Composite中定义组合相关方法
  • 透明式优点:客户端代码简洁,无需类型判断
  • 安全式优点:编译时类型安全,避免运行时异常
  • 推荐:优先使用透明式,客户端代码更简洁
问题:如何优化组合模式的递归操作性能?

答案要点:

// 1. 使用迭代代替递归
public void operation() {
    Stack<Component> stack = new Stack<>();
    stack.push(this);
  
    while (!stack.isEmpty()) {
        Component component = stack.pop();
        component.performOperation();
      
        if (component instanceof Composite) {
            for (Component child : ((Composite) component).getChildren()) {
                stack.push(child);
            }
        }
    }
}

// 2. 使用访问者模式
public interface Visitor {
    void visit(Leaf leaf);
    void visit(Composite composite);
}

// 3. 缓存计算结果
public abstract class Component {
    private boolean calculated = false;
    private Object cachedResult;
  
    public Object getResult() {
        if (!calculated) {
            cachedResult = calculate();
            calculated = true;
        }
        return cachedResult;
    }
}

4. Spring中的应用

问题:Spring中如何使用组合模式?

答案要点:

// 1. BeanDefinition层次结构
public interface BeanDefinition {
    void setParentName(String parentName);
    String getParentName();
    // ... 其他方法
}

// 2. Security权限层次结构
public interface ConfigAttribute {
    String getAttribute();
}

// 3. HandlerMapping层次结构
public interface HandlerMapping {
    HandlerExecutionChain getHandler(HttpServletRequest request);
}

// 4. AOP切面层次结构
public interface Pointcut {
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
}

5. 设计原则相关

问题:组合模式体现了哪些设计原则?

答案要点:

  • 开闭原则:可以添加新的叶子节点和组合节点
  • 单一职责:每个类只负责一个职责
  • 里氏替换:叶子节点和组合节点可以替换抽象构件
  • 组合优于继承:使用组合关系而不是继承关系

6. 实际应用场景

问题:组合模式适用于哪些场景?

答案要点:

  • 文件系统:文件和文件夹的层次结构
  • 组织架构:员工和管理者的层次结构
  • 菜单系统:菜单和菜单项的层次结构
  • 图形绘制:简单图形和复合图形的层次结构
  • XML解析:XML元素的层次结构
  • 权限管理:权限和角色的层次结构

7. 与其他模式的对比

问题:组合模式与装饰器模式的区别?

答案要点:

  • 目的:组合模式是结构组合,装饰器模式是功能增强
  • 关系:组合模式是部分-整体关系,装饰器模式是包装关系
  • 结构:组合模式是树形结构,装饰器模式是链式结构
  • 使用场景:组合模式用于层次结构,装饰器模式用于功能扩展
问题:组合模式与迭代器模式的关系?

答案要点:

  • 组合关系:组合模式经常与迭代器模式结合使用
  • 遍历需求:组合结构需要遍历所有节点
  • 实现方式:可以使用迭代器模式遍历组合结构
  • 性能优化:迭代器模式可以优化组合结构的遍历性能

总结

组合模式是一种重要的结构型设计模式,它通过将对象组合成树形结构,实现了部分-整体的层次结构,并提供了统一的接口。

核心优势

  1. 统一接口:叶子节点和组合节点使用相同接口
  2. 递归结构:支持树形结构的递归操作
  3. 透明性:客户端无需区分叶子节点和组合节点
  4. 扩展性:易于添加新的叶子节点和组合节点

注意事项

  1. 性能考虑:深层递归可能影响性能
  2. 内存管理:复杂结构需要注意内存泄漏
  3. 设计复杂度:增加了系统的复杂度
  4. 类型安全:透明式模式可能产生运行时异常

在实际开发中,组合模式特别适用于需要表示部分-整体层次结构的场景,如文件系统、组织架构、菜单系统等。通过合理使用组合模式,可以大大提高系统的灵活性和可维护性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值