使用场景
组合模式的目标是让单个对象和对象的集合存在相同的接口
基础组合模式的实现代码
#include<iostream>
#include<vector>
using namespace std;
struct GraphicObject {
virtual void draw() = 0;
};
struct Circle :GraphicObject {
void draw() override
{
cout << "Circle" << endl;
}
};
struct group :GraphicObject {
string name;
vector<GraphicObject*> objects;
group(const string& name) :name{ name } {
}
void draw() override
{
cout << "group " << name << " contains: " << endl;
for (auto&& o : objects) o->draw();
}
};
int main() {
group root("root");
Circle c1, c2;
root.objects.push_back(&c1);
root.objects.push_back(&c2);
group gp1("gp1");
Circle c3, c4;
gp1.objects.push_back(&c3);
gp1.objects.push_back(&c4);
root.objects.push_back(&gp1);
root.draw();
return 0;
}
可以观察到,group是circle的集合,但是他们都有相同的接口:draw(),这是因为group 中存在一个指向基类向量的指针,所以group也可以是group的集合。
更复杂的组合模式
上个例子中只包括了集合包含元素,集合包含集合,可能有人好奇如果想要元素包含集合该怎么办呢?
实现代码
#include<iostream>
#include<vector>
using namespace std;
struct NeuronLayer;
struct Neuron;
template<typename Self>
struct SomeNeurons { //用来将属性转换为Neuron
virtual SomeNeurons* begin() = 0;
virtual SomeNeurons* end() = 0;
template<typename T>void connect_to(T& other) {
for (Neuron& from : *static_cast<Self*>(this)) { //基于范围的循环
for (Neuron& to : other) {
from.out.push_back(&to);
to.in.push_back(&from);
}
}
}
};
struct Neuron:public SomeNeurons<Neuron> //继承自模板类,以Neuron作为模板
{
vector<Neuron*>in, out;
unsigned int id;
Neuron()
{
static int id = 1;
this->id = id++;
}
Neuron* begin() override { return this; }
Neuron* end() override{ return this+1; }
};
struct NeuronLayer :vector<Neuron> { //模板类继承,这样NeuronLayer是一个向量
int count;
NeuronLayer(int count = 0) :count{count} {
while (count-- > 0) {
emplace_back(Neuron{});//emplace_back是vector继承来的函数
}
}
template<typename T>void connect_to(T& other) {
for (Neuron& from : *static_cast<NeuronLayer*>(this)) {
for (Neuron& to : other) {
from.out.push_back(&to);
to.in.push_back(&from);
}
}
}
};
int main() {
Neuron neuron, neuron1;
NeuronLayer layer, layer1;
neuron.connect_to(neuron1);
neuron.connect_to(layer);
layer.connect_to(neuron);
layer.connect_to(layer1);
return 0;
}
上个例子中layer是neuron向量继承得到的,通过调用connect_to,既可以实现层与层的连接,也可以实现神经元与神经元的连接,使用到了模板成员函数、模板类
本例中还有到了范围循环,被循环的对象需要存在begin、end函数,由于NeuronLayer是由模板类vector继承而来,所以无需定义,Neuron则需要通过以下方法定义:
Neuron* begin() override { return this; }
Neuron* end() override{ return this+1; }
模板类
模板类的形式为
template<typename Self>
class{
};
具体类型Self可以通过运行中的参数指定
总结
组合模式通过显式使用接口或鸭子类型(仿照实现begin、end伪装成集合)实现单个类型与类型集合的转换接口