StructuralC++verifiedVerified
Composite Pattern in C++
Composes objects into tree structures to represent part-whole hierarchies, letting clients treat individual objects and compositions uniformly.
How to Implement the Composite Pattern in C++
1Step 1: Define the Component interface
class FileSystemEntry {
public:
virtual ~FileSystemEntry() = default;
virtual std::string name() const = 0;
virtual int size() const = 0;
virtual void print(int indent = 0) const = 0;
};2Step 2: Leaf
class File : public FileSystemEntry {
std::string name_;
int size_;
public:
File(std::string name, int size) : name_(std::move(name)), size_(size) {}
std::string name() const override { return name_; }
int size() const override { return size_; }
void print(int indent = 0) const override {
std::cout << std::string(indent, ' ') << name_ << " (" << size_ << "B)\n";
}
};3Step 3: Composite
class Directory : public FileSystemEntry {
std::string name_;
std::vector<std::unique_ptr<FileSystemEntry>> children_;
public:
explicit Directory(std::string name) : name_(std::move(name)) {}
void add(std::unique_ptr<FileSystemEntry> entry) {
children_.push_back(std::move(entry));
}
std::string name() const override { return name_; }
int size() const override {
int total = 0;
for (const auto& child : children_) total += child->size();
return total;
}
void print(int indent = 0) const override {
std::cout << std::string(indent, ' ') << name_ << "/\n";
for (const auto& child : children_) child->print(indent + 2);
}
};
int main() {
auto root = std::make_unique<Directory>("root");
root->add(std::make_unique<File>("readme.txt", 100));
auto src = std::make_unique<Directory>("src");
src->add(std::make_unique<File>("main.cpp", 500));
src->add(std::make_unique<File>("util.cpp", 300));
root->add(std::move(src));
root->print();
std::cout << "Total size: " << root->size() << "B\n";
}#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <format>
#include <functional>
#include <numeric>
#include <algorithm>
#include <stdexcept>
// [step] Define the Component interface with traversal
class UIComponent {
public:
virtual ~UIComponent() = default;
virtual std::string render(int depth = 0) const = 0;
virtual int componentCount() const = 0;
virtual void traverse(std::function<void(const UIComponent&, int)> visitor,
int depth = 0) const = 0;
virtual std::string type() const = 0;
virtual std::string id() const = 0;
};
// [step] Leaf components
class Label : public UIComponent {
std::string id_, text_;
public:
Label(std::string id, std::string text)
: id_(std::move(id)), text_(std::move(text)) {}
std::string render(int depth = 0) const override {
return std::format("{}<label id='{}'>{}</label>",
std::string(depth * 2, ' '), id_, text_);
}
int componentCount() const override { return 1; }
void traverse(std::function<void(const UIComponent&, int)> v, int d) const override {
v(*this, d);
}
std::string type() const override { return "Label"; }
std::string id() const override { return id_; }
};
class Input : public UIComponent {
std::string id_, inputType_, placeholder_;
public:
Input(std::string id, std::string type, std::string placeholder)
: id_(std::move(id)), inputType_(std::move(type)),
placeholder_(std::move(placeholder)) {}
std::string render(int depth = 0) const override {
return std::format("{}<input id='{}' type='{}' placeholder='{}'/>",
std::string(depth * 2, ' '), id_, inputType_, placeholder_);
}
int componentCount() const override { return 1; }
void traverse(std::function<void(const UIComponent&, int)> v, int d) const override {
v(*this, d);
}
std::string type() const override { return "Input"; }
std::string id() const override { return id_; }
};
// [step] Composite container
class Container : public UIComponent {
std::string id_, tag_;
std::vector<std::unique_ptr<UIComponent>> children_;
public:
Container(std::string id, std::string tag = "div")
: id_(std::move(id)), tag_(std::move(tag)) {}
Container& add(std::unique_ptr<UIComponent> child) {
children_.push_back(std::move(child));
return *this;
}
std::string render(int depth = 0) const override {
std::string indent(depth * 2, ' ');
std::string html = std::format("{}<{} id='{}'>\n", indent, tag_, id_);
for (const auto& child : children_)
html += child->render(depth + 1) + "\n";
html += std::format("{}</{}>", indent, tag_);
return html;
}
int componentCount() const override {
int count = 1;
for (const auto& child : children_) count += child->componentCount();
return count;
}
void traverse(std::function<void(const UIComponent&, int)> visitor,
int depth = 0) const override {
visitor(*this, depth);
for (const auto& child : children_)
child->traverse(visitor, depth + 1);
}
std::string type() const override { return "Container"; }
std::string id() const override { return id_; }
// Find by ID
const UIComponent* find(const std::string& targetId) const {
if (id_ == targetId) return this;
for (const auto& child : children_) {
if (child->id() == targetId) return child.get();
if (auto* container = dynamic_cast<const Container*>(child.get())) {
if (auto* found = container->find(targetId)) return found;
}
}
return nullptr;
}
};
// [step] Demonstrate composite rendering and traversal
int main() {
auto form = std::make_unique<Container>("login-form", "form");
auto nameGroup = std::make_unique<Container>("name-group");
nameGroup->add(std::make_unique<Label>("name-label", "Name:"));
nameGroup->add(std::make_unique<Input>("name-input", "text", "Enter name"));
auto emailGroup = std::make_unique<Container>("email-group");
emailGroup->add(std::make_unique<Label>("email-label", "Email:"));
emailGroup->add(std::make_unique<Input>("email-input", "email", "Enter email"));
form->add(std::move(nameGroup));
form->add(std::move(emailGroup));
// Render the tree
std::cout << form->render() << "\n\n";
// Traverse and count
std::cout << std::format("Total components: {}\n", form->componentCount());
// Find by ID
auto* found = form->find("email-input");
if (found)
std::cout << std::format("Found: {} (type={})\n", found->id(), found->type());
// Traverse with visitor
std::cout << "\nComponent tree:\n";
form->traverse([](const UIComponent& comp, int depth) {
std::cout << std::format("{}[{}] {}\n",
std::string(depth * 2, ' '), comp.type(), comp.id());
});
}Composite Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Composite Pattern in the Real World
“A company’s org chart is a composite structure. An individual employee (leaf) has a salary and a name. A department (composite) also has a name and a budget—calculated by summing the salaries of all its members, which may themselves be other departments. HR can call ‘get budget’ on the CEO’s node and the entire tree is summed recursively.”