最近摸索出來的心得,先來看看傳統的 Composite design pattern要怎麼作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <vector> #include <memory> using namespace std;
class Component { public: virtual ~Component() = default; };
class Leaf : public Component { };
class Composite : public Component { vector<unique_ptr<Component>> children; public: void add(Component *ele) { children.push_back(std::unique_ptr<Component>(ele)); } };
|
這個方法不差,不過還是有幾點可以改良的
– 每個Subtype都必須繼承Component,就算沒有任何is-a的關聯性還是必須這麼作
– 當要Clone一份物件出來的話,需要另外一個Prototype Pattern
例如
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
| class Component { public: virtual ~Component() = default; virtual Component* clone() = 0; };
class Leaf : public Component { public: Component* clone() override { return new Leaf(); } };
class Composite : public Component { vector<unique_ptr<Component>> children; public: void add(Component *ele) { children.push_back(std::unique_ptr<Component>(ele)); } Component* clone() override { Composite* composite = new Composite(); for (const auto &child : children) composite->children.push_back( std::unique_ptr<Component>(child->clone())); return composite; } };
|
每個都這麼作實在很醜,並且又容易錯
Solution based on std::variant
使用std::variant
可以解決上面的問題,乾淨俐落
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Leaf; class Composite; using CompositeVar = variant<Leaf, Composite>; class Leaf final { public: Leaf() = default; Leaf(const Leaf&) = default; };
class Composite final { vector<CompositeVar> children; public: Composite() = default; Composite(const Composite&) = default; void add(const CompositeVar &ele) { children.push_back(ele); } };
|
不過這也不是萬用解,當你的CompositeVar
中的type數目是有限的,可以採用這個方式
不然就是每加入一種Type,就必須重新編譯一次,反而沒有舊版的彈性