【设计模式】【结构型模式(Structural Patterns)】之组合模式(Composite Pattern)
1. 设计模式原理说明
组合模式(Composite Pattern) 是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得用户对单个对象和组合对象的使用具有一致性。这意味着无论处理的是单一对象还是复合对象,都可以使用相同的接口进行操作。
主要角色
- Component(组件):定义了树中每个对象的接口,可以是叶子节点或容器节点。
- Leaf(叶子节点):表示树中的末端节点,没有子节点,实现
Component
接口的具体行为。 - Composite(容器节点):表示树中的分支节点,可以包含一个或多个子节点,实现了
Component
接口的行为,并负责管理子节点的添加、删除等操作。 - Client(客户端):通过
Component
接口与所有对象交互,无需关心对象是叶子节点还是容器节点。
2. UML 类图及解释
UML 类图
+-----------------+ +-----------------+
| Component | | Leaf |
|-----------------| |-----------------|
| + operation(): void| | + operation(): void|
+-----------------+ +-----------------+
^ ^
| |
| |
v v
+-----------------+
| Composite |
|-----------------|
| - children: List<Component> |
| + add(child: Component): void|
| + remove(child: Component): void|
| + getChild(index: int): Component|
| + operation(): void |
+-----------------+
类图解释
- Component:定义了树中每个对象的接口,可以是叶子节点或容器节点。客户端通过这个接口与所有对象交互。
- Leaf:表示树中的末端节点,没有子节点,实现
Component
接口的具体行为。 - Composite:表示树中的分支节点,可以包含一个或多个子节点,实现了
Component
接口的行为,并负责管理子节点的添加、删除等操作。 - Client:通过
Component
接口与所有对象交互,无需关心对象是叶子节点还是容器节点。
3. 代码案例及逻辑详解
Java 代码案例
// 组件接口
interface Component {
void operation();
}
// 叶子节点
class Leaf implements Component {
@Override
public void operation() {
System.out.println("Leaf operation");
}
}
// 容器节点
class Composite implements Component {
private List<Component> children = new ArrayList<>();
public void add(Component component) {
children.add(component);
}
public void remove(Component component) {
children.remove(component);
}
public Component getChild(int index) {
return children.get(index);
}
@Override
public void operation() {
for (Component child : children) {
child.operation();
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Composite root = new Composite();
Composite branch = new Composite();
Leaf leaf1 = new Leaf();
Leaf leaf2 = new Leaf();
branch.add(leaf1);
branch.add(leaf2);
root.add(branch);
root.operation();
// 输出:
// Leaf operation
// Leaf operation
}
}
C++ 代码案例
#include <iostream>
#include <vector>
// 组件接口
class Component {
public:
virtual void operation() = 0;
virtual ~Component() {}
};
// 叶子节点
class Leaf : public Component {
public:
void operation() override {
std::cout << "Leaf operation" << std::endl;
}
};
// 容器节点
class Composite : public Component {
private:
std::vector<Component*> children;
public:
void add(Component* component) {
children.push_back(component);
}
void remove(Component* component) {
children.erase(std::remove(children.begin(), children.end(), component), children.end());
}
Component* getChild(int index) {
return children[index];
}
void operation() override {
for (Component* child : children) {
child->operation();
}
}
~Composite() {
for (Component* child : children) {
delete child;
}
}
};
// 客户端
int main() {
Composite* root = new Composite();
Composite* branch = new Composite();
Leaf* leaf1 = new Leaf();
Leaf* leaf2 = new Leaf();
branch->add(leaf1);
branch->add(leaf2);
root->add(branch);
root->operation();
// 输出:
// Leaf operation
// Leaf operation
delete root;
return 0;
}
Python 代码案例
from abc import ABC, abstractmethod
from typing import List
# 组件接口
class Component(ABC):
@abstractmethod
def operation(self):
pass
# 叶子节点
class Leaf(Component):
def operation(self):
print("Leaf operation")
# 容器节点
class Composite(Component):
def __init__(self):
self.children: List[Component] = []
def add(self, component: Component):
self.children.append(component)
def remove(self, component: Component):
self.children.remove(component)
def get_child(self, index: int) -> Component:
return self.children[index]
def operation(self):
for child in self.children:
child.operation()
# 客户端
if __name__ == "__main__":
root = Composite()
branch = Composite()
leaf1 = Leaf()
leaf2 = Leaf()
branch.add(leaf1)
branch.add(leaf2)
root.add(branch)
root.operation()
# 输出:
# Leaf operation
# Leaf operation
Go 代码案例
package main
import "fmt"
// 组件接口
type Component interface {
Operation()
}
// 叶子节点
type Leaf struct{}
func (l *Leaf) Operation() {
fmt.Println("Leaf operation")
}
// 容器节点
type Composite struct {
children []Component
}
func (c *Composite) Add(child Component) {
c.children = append(c.children, child)
}
func (c *Composite) Remove(child Component) {
for i, ch := range c.children {
if ch == child {
c.children = append(c.children[:i], c.children[i+1:]...)
break
}
}
}
func (c *Composite) GetChild(index int) Component {
return c.children[index]
}
func (c *Composite) Operation() {
for _, child := range c.children {
child.Operation()
}
}
// 客户端
func main() {
root := &Composite{}
branch := &Composite{}
leaf1 := &Leaf{}
leaf2 := &Leaf{}
branch.Add(leaf1)
branch.Add(leaf2)
root.Add(branch)
root.Operation()
// 输出:
// Leaf operation
// Leaf operation
}
4. 总结
组合模式 是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得用户对单个对象和组合对象的使用具有一致性,从而提高了系统的灵活性和可扩展性。
主要优点
- 一致性:用户可以通过相同的接口操作单个对象和组合对象,使得客户端代码更加简洁和一致。
- 灵活性:组合模式使得树形结构的构建更加灵活,可以动态地添加和删除节点。
- 易于扩展:新增节点类型时,只需实现
Component
接口即可,无需修改现有代码。
主要缺点
- 增加了系统的复杂性:组合模式引入了更多的类和接口,增加了系统的复杂性。
- 客户端需要谨慎处理空指针:如果客户端代码不小心访问了空指针,可能会导致运行时错误。
适用场景
- 当需要表示部分-整体层次结构时。
- 当希望用户可以忽略组合对象与单个对象的不同,统一使用组合结构中的所有对象。
- 当系统中存在复杂的树形结构,需要频繁地添加、删除节点时。