Padrão Composite
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 101 de 104.
O padrão Composite permite que você trate objetos individuais e grupos de objetos de forma uniforme. Ele compõe objetos em estruturas de árvore onde tanto elementos únicos quanto containers de elementos compartilham a mesma interface. Isso é ideal para representar hierarquias como sistemas de arquivos, organogramas ou componentes de UI.
O padrão tem três partes principais: uma interface Component que define operações comuns, classes Leaf que representam objetos individuais e classes Composite que contêm filhos e delegam operações a eles:
#include <iostream>
#include <memory>
#include <vector>
#include <string>
// Interface Component
class FileSystemItem {
public:
virtual void display(int indent = 0) const = 0;
virtual int getSize() const = 0;
virtual ~FileSystemItem() = default;
};
// Leaf - representa arquivos individuais
class File : public FileSystemItem {
std::string name;
int size;
public:
File(const std::string& n, int s) : name(n), size(s) {}
void display(int indent = 0) const override {
std::cout << std::string(indent, ' ') << name
<< " (" << size << " KB)\n";
}
int getSize() const override { return size; }
};
// Composite - contém outros componentes
class Folder : public FileSystemItem {
std::string name;
std::vector<std::shared_ptr<FileSystemItem>> children;
public:
Folder(const std::string& n) : name(n) {}
void add(std::shared_ptr<FileSystemItem> item) {
children.push_back(item);
}
void display(int indent = 0) const override {
std::cout << std::string(indent, ' ') << "[" << name << "]\n";
for (const auto& child : children) {
child->display(indent + 2);
}
}
int getSize() const override {
int total = 0;
for (const auto& child : children) {
total += child->getSize();
}
return total;
}
};O composite Folder armazena filhos e implementa operações iterando através deles. Quando você chama getSize() em uma pasta, ele calcula recursivamente o tamanho total de todos os itens contidos. O código cliente não precisa saber se está trabalhando com um arquivo ou uma pasta — ambos respondem à mesma interface.
Use o Composite quando você precisar representar hierarquias parte-todo e quiser que os clientes tratem objetos individuais e composições de forma uniforme.
Desafio
FácilVamos construir um sistema de Organograma usando o padrão Composite. Você criará uma hierarquia onde tanto funcionários individuais quanto departamentos (que contêm outros funcionários ou subdepartamentos) podem ser tratados de forma uniforme. Isso reflete como as empresas reais são estruturadas — departamentos contêm pessoas e outros departamentos, formando uma estrutura de árvore.
Você organizará seu código em três arquivos:
OrgComponent.h: Defina a interface do componente que tanto os funcionários quanto os departamentos implementarão.Crie uma classe abstrata
OrgComponentcom:getName()— retorna o nome do componentegetSalary()— retorna o salário total (para funcionários, seu próprio salário; para departamentos, a soma de todos os salários contidos)display(int indent = 0)— exibe o componente com a indentação adequada
Inclua um destrutor virtual.
Organization.h: Implemente as classes leaf (folha) e composite (composta).Crie uma classe
Employee(a folha) que armazena um nome e um salário. Seu métododisplay()deve imprimir as informações do funcionário neste formato:[indent spaces]- [name] ($[salary])Crie uma classe
Department(a composta) que armazena um nome e uma coleção de filhosOrgComponentusandostd::shared_ptr. Implemente:add(std::shared_ptr<OrgComponent> component)— adiciona um filho ao departamentogetSalary()— calcula recursivamente o salário total de todos os membrosdisplay()— imprime o nome do departamento entre colchetes e, em seguida, exibe todos os filhos com indentação aumentada (adicione 2 espaços por nível)
O formato de exibição do departamento deve ser:
[indent spaces][Department Name] [children displayed with indent + 2]main.cpp: Construa e exiba uma estrutura organizacional.Leia quatro entradas:
- Nome da empresa (string)
- Nome do departamento (string)
- Nome e salário do primeiro funcionário (formato:
name,salary) - Nome e salário do segundo funcionário (formato:
name,salary)
Construa esta estrutura: Crie uma empresa (departamento de nível superior), adicione um subdepartamento a ela e adicione ambos os funcionários a esse subdepartamento. Em seguida, exiba toda a organização e imprima o salário total da empresa.
Após exibir a estrutura, imprima:
Total Salary: $[amount]
Por exemplo, com as entradas TechCorp, Engineering, Alice,75000 e Bob,65000:
[TechCorp]
[Engineering]
- Alice ($75000)
- Bob ($65000)
Total Salary: $140000Com as entradas StartupInc, Development, Carol,80000 e Dave,70000:
[StartupInc]
[Development]
- Carol ($80000)
- Dave ($70000)
Total Salary: $150000Observe como o getSalary() funciona uniformemente, seja chamado em um funcionário ou em um departamento — o departamento agrega automaticamente os salários de todos os seus membros. O código do cliente não precisa distinguir entre funcionários individuais e departamentos inteiros ao calcular totais ou exibir a hierarquia.
Folha de consulta
O padrão Composite trata objetos individuais e grupos de objetos de forma uniforme, compondo-os em estruturas de árvore onde tanto elementos únicos quanto containers compartilham a mesma interface.
O padrão possui três partes principais:
- Component: Uma interface que define operações comuns
- Leaf: Classes que representam objetos individuais
- Composite: Classes que contêm filhos e delegam operações a eles
Exemplo de implementação de um sistema de arquivos:
#include <iostream>
#include <memory>
#include <vector>
#include <string>
// Interface Component
class FileSystemItem {
public:
virtual void display(int indent = 0) const = 0;
virtual int getSize() const = 0;
virtual ~FileSystemItem() = default;
};
// Leaf - representa arquivos individuais
class File : public FileSystemItem {
std::string name;
int size;
public:
File(const std::string& n, int s) : name(n), size(s) {}
void display(int indent = 0) const override {
std::cout << std::string(indent, ' ') << name
<< " (" << size << " KB)\n";
}
int getSize() const override { return size; }
};
// Composite - contém outros componentes
class Folder : public FileSystemItem {
std::string name;
std::vector<std::shared_ptr<FileSystemItem>> children;
public:
Folder(const std::string& n) : name(n) {}
void add(std::shared_ptr<FileSystemItem> item) {
children.push_back(item);
}
void display(int indent = 0) const override {
std::cout << std::string(indent, ' ') << "[" << name << "]\n";
for (const auto& child : children) {
child->display(indent + 2);
}
}
int getSize() const override {
int total = 0;
for (const auto& child : children) {
total += child->getSize();
}
return total;
}
};O composite armazena filhos usando std::shared_ptr e implementa operações iterando através deles. Operações como getSize() funcionam recursivamente, calculando totais em toda a hierarquia.
Use o Composite quando precisar representar hierarquias parte-todo e quiser que os clientes tratem objetos individuais e composições de forma uniforme.
Experimente você mesmo
#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include "Organization.h"
int main() {
// Ler as entradas
std::string companyName;
std::string departmentName;
std::string employee1Input;
std::string employee2Input;
std::getline(std::cin, companyName);
std::getline(std::cin, departmentName);
std::getline(std::cin, employee1Input);
std::getline(std::cin, employee2Input);
// Lambda auxiliar para analisar o formato "name,salary"
auto parseEmployee = [](const std::string& input) -> std::pair<std::string, int> {
size_t commaPos = input.find(',');
std::string name = input.substr(0, commaPos);
int salary = std::stoi(input.substr(commaPos + 1));
return {name, salary};
};
auto [name1, salary1] = parseEmployee(employee1Input);
auto [name2, salary2] = parseEmployee(employee2Input);
// TODO: Criar a empresa como um Department de nível superior
// TODO: Criar um subdepartamento
// TODO: Criar dois objetos Employee usando os dados analisados
// TODO: Adicionar funcionários ao subdepartamento
// TODO: Adicionar o subdepartamento à empresa
// TODO: Exibir toda a estrutura da organização
// TODO: Imprimir o salário total no formato: Total Salary: $[amount]
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP
Arquivos ExternosBuild e Compilação em C++Arquivos Header e SourceNamespaces e EscopoIntrodução a OOP em C++Classes vs ObjetosO Ponteiro 'this'Métodos (Funções Membro)Atributos (Membros de Dados)Básico de Ctors e DtorsRecapitulação - Calculadora Simples4Propriedades de Classe
Membros de Instância vs EstáticosGetters e SettersFunções de Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRecapitulação - Gerenciador de Conta Bancária7Herança
Herança BásicaNíveis de Acesso na HerançaOrdem de Chamada de Ctor e DtorSobrescrita de MétodosFunções Virtuais e VTableHerança MúltiplaHerança VirtualRecapitulação - Hierarquia de Funcionários2Gerenciamento de Memória
Memória Stack vs HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Smart Pointers em C++RAII em C++Recapitulação - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de Acesso em C++Especificadores de Acesso em DetalhesOcultação de InformaçãoStruct vs ClassClasses Aninhadas e InternasRecapitulação - Sistema de Registros de Alunos8Polimorfismo
Polimorfismo de Compilação vs Tempo de ExecuçãoSobrecarga de FunçõesFunções Virtuais RevisitadasFunções Virtuais PurasClasses AbstratasDesign de Interfaces em C++Dynamic Casting e RTTIRecapitulação - Calculadora de Formas11Conceitos Avançados de POO
Composição vs HerançaMixins via CRTPPimpl IdiomType ErasureEnum Classes e Tipagem ForteTratamento de Exceções em POOHierarquias de Exceções Personalizadas14Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como um Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentaçãoListas de Inicialização do ConstrutorConstrutores DelegadosMergulho Profundo em DestrutoresRegra dos Três / Cinco / ZeroRecapitulação - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de StreamSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçãoTemplates de ClasseEspecialização de TemplatesTemplates VariádicosFundamentos de SFINAE e Type TraitsRecapitulação - Container Genérico