Menu
Coddy logo textTech

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 tratar objetos individuais e grupos de objetos de maneira uniforme. Ele compõe objetos em estruturas de árvore nas quais tanto elementos únicos quanto contêineres 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 elementos filhos e delegam operações a eles:

#include <iostream>
#include <memory>
#include <vector>
#include <string>

// Interface do componente
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 composto Folder armazena os filhos e implementa operações iterando por eles. 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 Composite quando precisar representar hierarquias parte-todo e quiser que os clientes tratem objetos individuais e composições de maneira uniforme.

challenge icon

Desafio

Fácil

Vamos criar um sistema de Organograma usando o padrão Composite. Você criará uma hierarquia na qual tanto funcionários individuais quanto departamentos (que contêm outros funcionários ou subdepartamentos) podem ser tratados uniformemente. 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 OrgComponent com:

    • getName(): retorna o nome do componente
    • getSalary(): retorna o salário total (para funcionários, o próprio salário; para departamentos, a soma de todos os salários contidos)
    • display(int indent = 0): exibe o componente com o recuo adequado

    Inclua um destrutor virtual.

  • Organization.h: implemente as classes folha e composta.

    Crie uma classe Employee (a folha) que armazene um nome e um salário. O método display() deve imprimir as informações do funcionário neste formato:

    [indent spaces]- [name] ($[salary])

    Crie uma classe Department (a composta) que armazene um nome e uma coleção de filhos OrgComponent usando std::shared_ptr. Implemente:

    • add(std::shared_ptr<OrgComponent> component): adiciona um filho ao departamento
    • getSalary(): calcula recursivamente o salário total de todos os membros
    • display(): imprime o nome do departamento entre colchetes e, em seguida, exibe todos os filhos com recuo aumentado (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: crie e exiba uma estrutura organizacional.

    Leia quatro entradas:

    1. Nome da empresa (string)
    2. Nome do departamento (string)
    3. Nome e salário do primeiro funcionário (formato: name,salary)
    4. Nome e salário do segundo funcionário (formato: name,salary)

    Crie esta estrutura: crie uma empresa (departamento de nível superior), adicione um subdepartamento a ela e adicione os dois funcionários a esse subdepartamento. Em seguida, exiba toda a organização e imprima o salário total da empresa.

    Depois de 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: $140000

Com as entradas StartupInc, Development, Carol,80000 e Dave,70000:

[StartupInc]
  [Development]
    - Carol ($80000)
    - Dave ($70000)
Total Salary: $150000

Observe como getSalary() funciona uniformemente quando chamado em um funcionário ou em um departamento: o departamento agrega automaticamente os salários de todos os seus membros. O código cliente não precisa distinguir entre funcionários individuais e departamentos inteiros ao calcular os totais ou exibir a hierarquia.

Experimente você mesmo

#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include "Organization.h"

int main() {
    // Ler 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 os funcionários ao subdepartamento
    
    // TODO: Adicionar o subdepartamento à empresa
    
    // TODO: Exibir a estrutura completa da organização
    
    // TODO: Imprimir o salário total no formato: Total Salary: $[amount]
    
    return 0;
}
quiz iconTeste seus conhecimentos

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

Pratique por conta própria: Compilador de C++ online