组合模式详解
目录
组合模式简介
定义
组合模式(Composite Pattern)是一种结构型设计模式,它将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
核心思想
- 统一接口:叶子节点和组合节点使用相同的接口
- 递归结构:支持树形结构的递归操作
- 透明性:客户端无需区分叶子节点和组合节点
- 整体-部分:将部分对象组合成整体对象
模式结构
- Component(抽象构件):为组合中的对象声明接口,在适当情况下实现所有类共有接口的默认行为
- Leaf(叶子构件):在组合中表示叶子节点对象,叶子节点没有子节点
- Composite(组合构件):定义有子部件的那些部件的行为,存储子部件,在Component接口中实现与子部件有关的操作
核心流程
组合模式流程图
基本实现流程
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();
}
}
面试高频点
面试知识点思维导图
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. 与其他模式的对比
问题:组合模式与装饰器模式的区别?
答案要点:
- 目的:组合模式是结构组合,装饰器模式是功能增强
- 关系:组合模式是部分-整体关系,装饰器模式是包装关系
- 结构:组合模式是树形结构,装饰器模式是链式结构
- 使用场景:组合模式用于层次结构,装饰器模式用于功能扩展
问题:组合模式与迭代器模式的关系?
答案要点:
- 组合关系:组合模式经常与迭代器模式结合使用
- 遍历需求:组合结构需要遍历所有节点
- 实现方式:可以使用迭代器模式遍历组合结构
- 性能优化:迭代器模式可以优化组合结构的遍历性能
总结
组合模式是一种重要的结构型设计模式,它通过将对象组合成树形结构,实现了部分-整体的层次结构,并提供了统一的接口。
核心优势
- 统一接口:叶子节点和组合节点使用相同接口
- 递归结构:支持树形结构的递归操作
- 透明性:客户端无需区分叶子节点和组合节点
- 扩展性:易于添加新的叶子节点和组合节点
注意事项
- 性能考虑:深层递归可能影响性能
- 内存管理:复杂结构需要注意内存泄漏
- 设计复杂度:增加了系统的复杂度
- 类型安全:透明式模式可能产生运行时异常
在实际开发中,组合模式特别适用于需要表示部分-整体层次结构的场景,如文件系统、组织架构、菜单系统等。通过合理使用组合模式,可以大大提高系统的灵活性和可维护性。
1290

被折叠的 条评论
为什么被折叠?



