创建型(5种) 工厂方法 抽象工厂模式 单例模式 建造者模式 原型模式
结构型(7种) 适配器模式 装饰器模式 代理模式 外观模式 桥接模式 组合模式 享元模式
行为型(11种) 策略模式 模板方法模式 观察者模式 迭代器模式 责任链模式 命令模式
备忘录模式 状态模式 访问者模式 中介者模式
组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示"部分-整体"的层次结构。这种模式使得客户端可以以一致的方式处理单个对象(叶子节点)和组合对象(容器节点),无需关心处理的是个体还是群体。组合模式使得你可以将对象看作是树形结构中的节点,节点可以是简单的对象,也可以是包含其他节点的复合对象,这样就能形成一个层次化的结构。
模式结构
组合模式主要涉及以下几个角色:
Component(抽象组件):
- 定义了所有对象(包括叶子节点和容器节点)共享的公共接口。这个接口规定了如何访问和管理对象的子部件。
- 通常会提供一个方法来添加、删除子组件,以及遍历子组件的方法。
Leaf(叶子组件):
- 是组合结构的终端节点,不包含任何子组件。
- 实现
Component
接口,但对于那些与子组件管理无关的方法(如添加、删除子组件),可以提供空实现或者抛出异常。Composite(容器组件):
- 包含一个或多个子组件,每个子组件也是
Component
的实例。- 实现
Component
接口,提供与管理子组件相关的方法的实际逻辑,如添加、删除子组件以及遍历子组件。- 可能会提供一些额外的方法来管理子组件集合,但这些方法通常不暴露给客户端。
工作原理
- 客户端 :通过
Component
接口与系统交互,无需区分处理的是叶子节点还是容器节点。- Component:定义了通用接口,为所有组件(包括叶子和容器)提供一致性。
- Leaf :实现
Component
接口,但不包含子组件,因此与子组件管理相关的操作为空或无效。- Composite :除了实现
Component
接口外,还持有子组件的集合,并提供操作子组件的方法。当客户端请求操作时,Composite
会递归地将请求传递给它的子组件。
优缺点
优点
- 单一职责原则:组合模式使得叶子节点和容器节点都遵循单一职责原则,各自专注于自己的功能。
- 透明性:客户端可以一致地处理单个对象和组合对象,无需知道处理的是叶子还是容器,提高了代码的透明性和简洁性。
- 易于扩展 :新类型的组件只需继承
Component
或实现相关接口即可加入到组合结构中,不影响已有代码。
缺点
- 设计复杂度增加:为了实现组合模式,需要设计额外的抽象层和接口,使得系统变得相对复杂。
- 递归操作可能导致性能问题:如果组合结构非常深,递归操作可能会导致栈溢出或效率下降。
适用场景
- 系统需要处理对象的"部分-整体"关系:当需要表示对象的层级结构时,组合模式可以很好地表示这种关系。
- 希望客户端以一致的方式处理单个对象和组合对象:组合模式使得客户端无需关心处理对象的具体类型,简化了客户端代码。
- 希望简化新组件类型的添加 :新的叶子节点或容器节点只需要符合
Component
接口即可轻松融入现有系统。
代码示例(以Java为例)
java
// 抽象组件
interface Component {
void add(Component component);
void remove(Component component);
void operation();
}
// 叶子节点
class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void add(Component component) {
throw new UnsupportedOperationException("Leaves cannot have children.");
}
@Override
public void remove(Component component) {
throw new UnsupportedOperationException("Leaves cannot have children.");
}
@Override
public void operation() {
System.out.println("Leaf " + name + " performing operation.");
}
}
// 容器节点
class Composite implements Component {
private List<Component> children = new ArrayList<>();
private String name;
public Composite(String name) {
this.name = name;
}
@Override
public void add(Component component) {
children.add(component);
}
@Override
public void remove(Component component) {
children.remove(component);
}
@Override
public void operation() {
System.out.println("Composite " + name + " performing operation.");
for (Component child : children) {
child.operation();
}
}
}
// 客户端代码
public class CompositePatternDemo {
public static void main(String[] args) {
Component root = new Composite("Root");
root.add(new Leaf("Leaf A"));
root.add(new Leaf("Leaf B"));
Component branch = new Composite("Branch");
branch.add(new Leaf("Leaf C"));
branch.add(new Leaf("Leaf D"));
root.add(branch);
root.operation();
}
}
在这个Java示例中:
Component
接口定义了所有组件(叶子和容器)的通用接口,包括添加、删除子组件和执行操作的方法。Leaf
类实现了Component
接口,但其add
和remove
方法抛出异常,表示叶子节点无法添加或删除子节点。operation
方法输出叶子节点执行操作的信息。Composite
类同样实现了Component
接口,并维护了一个List<Component>
来存储子组件。add
和remove
方法实现了对子组件的增删操作。operation
方法不仅执行自身操作,还递归地调用其子组件的operation
方法。- 客户端代码创建了一个树状结构,并通过调用根节点的
operation
方法,以一致的方式处理整个组合结构中的所有组件。
代码示例(以Python为例)
java
from abc import ABC, abstractmethod
class FileSystemObject(ABC):
"""抽象组件(Component)"""
def __init__(self, name):
self.name = name
@abstractmethod
def add(self, child):
pass
@abstractmethod
def remove(self, child):
pass
@abstractmethod
def get_child(self, index):
pass
@abstractmethod
def operation(self):
pass
class File(FileSystemObject):
"""叶子组件(Leaf)"""
def add(self, child):
raise TypeError("Cannot add children to a file")
def remove(self, child):
raise TypeError("Cannot remove children from a file")
def get_child(self, index):
raise IndexError("Files do not have children")
def operation(self):
return f"Performing operation on file: {self.name}"
class Directory(FileSystemObject):
"""复合组件(Composite)"""
def __init__(self, name):
super().__init__(name)
self.children = []
def add(self, child):
self.children.append(child)
def remove(self, child):
self.children.remove(child)
def get_child(self, index):
return self.children[index]
def operation(self):
result = f"Performing operation on directory: {self.name}\n"
for child in self.children:
result += child.operation() + "\n"
return result
# 客户端代码
if __name__ == "__main__":
root_dir = Directory("root")
dir_a = Directory("dir_a")
dir_b = Directory("dir_b")
file_1 = File("file_1.txt")
file_2 = File("file_2.txt")
root_dir.add(dir_a)
root_dir.add(dir_b)
dir_a.add(file_1)
dir_b.add(file_2)
print(root_dir.operation())
在这个Python示例中:
FileSystemObject
是抽象组件,使用abc
模块中的ABC
类和abstractmethod
装饰器定义了所有文件系统对象共有的接口,如添加、删除子对象和执行操作等。File
类作为叶子组件,继承自FileSystemObject
,实现了operation
方法,并且其add
、remove
和get_child
方法抛出异常,表示文件不能包含子对象。Directory
类作为复合组件,同样继承自FileSystemObject
,并且维护了一个children
列表来存储子对象(可以是文件或子目录)。add
、remove
和get_child
方法实现了对子对象的管理。operation
方法不仅执行自身操作,还递归地调用其子对象的operation
方法,从而处理整个目录树。
客户端代码创建了根目录、子目录以及文件,并建立了它们之间的层级关系。最后,通过调用根目录的operation
方法,以一致的方式处理整个文件系统的对象,无论是单个文件还是包含多个子对象的目录。