从文件系统、UI 树到宏命令,聊聊如何用统一接口抹平‘整体’与‘部分’的差异,让递归逻辑在对象内部自然生长。

你是个将军。

这是个假设。

但请你带入这个角色。

你想发起一场总攻。

你手下有 3 个师。

每个师有 4 个旅。

每个旅有 5 个团。

一直到最底层的列兵。

如果你的代码逻辑是这样的:

1
2
3
4
5
6
7
8
9
10
11
void General::attack() {
for (auto& division : divisions) {
for (auto& brigade : division.brigades) {
for (auto& regiment : brigade.regiments) {
// ... 写了五层循环
// 终于找到士兵
soldier.fire();
}
}
}
}

那你这个将军当得太累了。

你不仅要认识师长。

还得认识旅长。

还得知道团长下面是营长。

一旦编制改了。

比如团下面多加了个“特战队”。

你的 attack 方法就得重写。

这不叫指挥。

这叫微操。

真正的指挥是什么?

是你只管下令:“全军出击”。

师长听到命令。

传给旅长:“全旅出击”。

旅长传给团长。

一直传到列兵。

列兵没有下属了。

他听到命令。

就是扣动扳机。

对你来说。

指挥一个师。

和指挥一个兵。

动作应该是一样的。

这就是组合模式 (Composite)

它想解决的问题只有一个:

如何把“整体”和“部分”,当成同一个东西来用。


无论是排长,班长,士兵,大家都是军人,从代码来讲,我们可以抽象出一个abstructSoldier基类,把士兵当作叶子节点,排长为根节点,我们就构造出一颗树,只需要树根发号施令,并把命令依次传递给自己的子节点即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <vector>
#include <memory>

class abstructSoldier {
public:
virtual void attack() = 0;
virtual void addSubordinate(std::shared_ptr<abstructSoldier> soldier) {}
virtual ~abstructSoldier() {}

protected:
std::vector<std::shared_ptr<abstructSoldier>> m_soldiers;
};

class Soldier : public abstructSoldier {
public:
void attack() override {
std::cout << "我是士兵,发起冲锋" << std::endl;
}
};

class Commander : public abstructSoldier {
public:
void addSubordinate(std::shared_ptr<abstructSoldier> soldier) override {
m_soldiers.push_back(soldier);
}
void attack() override {
std::cout << "我是班长,大家随我上" << std::endl;
for(auto &item : m_soldiers) {
item->attack();
}
}
};

class PlatoonLeader : public abstructSoldier {
public:
void addSubordinate(std::shared_ptr<abstructSoldier> soldier) override {
m_soldiers.push_back(soldier);
}
void attack() override {
std::cout << "我是排长,指挥大家发起进攻" << std::endl;
for(auto &item : m_soldiers) {
item->attack();
}
}
};

int main() {
std::shared_ptr<abstructSoldier> p = std::make_shared<PlatoonLeader>();

for(int i = 0; i < 3; i++) {
std::shared_ptr<abstructSoldier> c = std::make_shared<Commander>();
for(int j = 0; j < 15; j++) {
std::shared_ptr<abstructSoldier> s = std::make_shared<Soldier>();
c->addSubordinate(s);
}
p->addSubordinate(c);
}

p->attack();

return 0;
}