Menu
Coddy logo textTech

Padrão Composite

Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 61 de 70.

O padrão Composite é um padrão estrutural que permite compor objetos em estruturas de árvore e tratar objetos individuais e grupos de objetos de maneira uniforme. É perfeito para representar hierarquias parte-todo onde você deseja trabalhar tanto com itens individuais quanto com coleções através da mesma interface.

O padrão possui três componentes principais: uma interface de componente que define operações comuns, classes folha que representam objetos individuais e classes compostas que contêm filhos e implementam a mesma interface:

public interface IFileSystemItem
{
    string Name { get; }
    int GetSize();
}

public class File : IFileSystemItem
{
    public string Name { get; }
    private int _size;
    
    public File(string name, int size)
    {
        Name = name;
        _size = size;
    }
    
    public int GetSize() => _size;
}

public class Folder : IFileSystemItem
{
    public string Name { get; }
    private List<IFileSystemItem> _items = new List<IFileSystemItem>();
    
    public Folder(string name) => Name = name;
    
    public void Add(IFileSystemItem item) => _items.Add(item);
    
    public int GetSize() => _items.Sum(item => item.GetSize());
}

A beleza deste padrão é que os clientes não precisam saber se estão trabalhando com um único arquivo ou com uma hierarquia de pastas inteira:

var root = new Folder("root");
root.Add(new File("doc.txt", 100));

var subFolder = new Folder("images");
subFolder.Add(new File("photo.jpg", 500));
subFolder.Add(new File("icon.png", 50));

root.Add(subFolder);

Console.WriteLine(root.GetSize());  // 650

O padrão Composite é ideal para sistemas de arquivos, árvores de componentes de UI, organogramas ou qualquer estrutura onde contêineres e conteúdos devam ser tratados da mesma forma. Ele simplifica o código do cliente ao eliminar a necessidade de distinguir entre elementos simples e complexos.

challenge icon

Desafio

Fácil

Vamos construir um sistema de organograma usando o padrão Composite. Você criará uma estrutura onde tanto funcionários individuais quanto departamentos (que contêm funcionários ou outros departamentos) podem ser tratados de forma uniforme, permitindo calcular o custo salarial total em qualquer nível da hierarquia.

Você organizará seu código em três arquivos:

  • OrgComponent.cs: Defina uma interface IOrgComponent no namespace Organization com dois membros: uma propriedade Name (string) e um método GetSalary() que retorna um número inteiro. Esta interface representa o contrato comum que tanto os funcionários individuais quanto os departamentos seguirão.
  • OrgClasses.cs: Crie duas classes no mesmo namespace:
    • Employee - uma classe folha (leaf) que representa um trabalhador individual. Ela recebe um nome e um salário em seu construtor e implementa a interface retornando seu próprio salário.
    • Department - uma classe composta (composite) que representa um grupo. Ela recebe um nome em seu construtor e mantém uma lista de itens IOrgComponent. Inclua um método Add(IOrgComponent component) para adicionar membros. Seu GetSalary() deve retornar a soma dos salários de todos os seus membros.
  • Program.cs: Construa uma estrutura organizacional baseada na entrada e calcule o salário total. Você criará departamentos e funcionários, os aninhará adequadamente e demonstrará como o padrão composite permite tratar toda a hierarquia de forma uniforme.

Você receberá as seguintes entradas:

  • O número de componentes a serem criados
  • Para cada componente: o tipo (employee ou department), seguido pelo nome e, para funcionários, seu salário. Para departamentos, você também receberá o nome do departamento pai (ou root se for um departamento de nível superior)

Formato de entrada para cada componente:

employee
{name}
{salary}
{parent_department}

department
{name}
{parent_department}

Após construir a estrutura, imprima o salário total do departamento raiz com o formato {root_name} Total Salary: {amount}.

Por exemplo, se as entradas forem:

5
department
Engineering
root
employee
Alice
5000
Engineering
employee
Bob
4500
Engineering
department
QA
Engineering
employee
Charlie
4000
QA

A saída deve ser:

Engineering Total Salary: 13500

O departamento Engineering contém Alice (5000), Bob (4500) e o departamento QA. QA contém Charlie (4000). Quando você chama GetSalary() em Engineering, ele calcula recursivamente o total de todos os componentes aninhados - demonstrando como o padrão Composite permite trabalhar com estruturas de árvore complexas através de uma interface simples e uniforme!

Folha de consulta

O padrão Composite é um padrão estrutural que compõe objetos em estruturas de árvore e trata objetos individuais e grupos uniformemente por meio da mesma interface. É ideal para hierarquias parte-todo.

O padrão possui três componentes principais:

  • Interface Component: Define operações comuns
  • Classes Leaf: Representam objetos individuais
  • Classes Composite: Contêm filhos e implementam a mesma interface

Exemplo básico de implementação:

// Interface Component
public interface IFileSystemItem
{
    string Name { get; }
    int GetSize();
}

// Classe Leaf
public class File : IFileSystemItem
{
    public string Name { get; }
    private int _size;
    
    public File(string name, int size)
    {
        Name = name;
        _size = size;
    }
    
    public int GetSize() => _size;
}

// Classe Composite
public class Folder : IFileSystemItem
{
    public string Name { get; }
    private List<IFileSystemItem> _items = new List<IFileSystemItem>();
    
    public Folder(string name) => Name = name;
    
    public void Add(IFileSystemItem item) => _items.Add(item);
    
    public int GetSize() => _items.Sum(item => item.GetSize());
}

Uso - os clientes trabalham com itens individuais e coleções de forma uniforme:

var root = new Folder("root");
root.Add(new File("doc.txt", 100));

var subFolder = new Folder("images");
subFolder.Add(new File("photo.jpg", 500));
subFolder.Add(new File("icon.png", 50));

root.Add(subFolder);

Console.WriteLine(root.GetSize());  // 650

O padrão Composite é útil para sistemas de arquivos, árvores de componentes de UI, organogramas ou qualquer estrutura onde contêineres e conteúdos devam ser tratados da mesma forma.

Experimente você mesmo

using System;
using System.Collections.Generic;
using Organization;

class Program
{
    public static void Main(string[] args)
    {
        int n = Convert.ToInt32(Console.ReadLine());
        
        // TODO: Criar um dicionário para armazenar departamentos por nome
        // Isso ajudará você a encontrar departamentos pai ao adicionar componentes
        Dictionary<string, Department> departments = new Dictionary<string, Department>();
        
        // TODO: Manter o controle do departamento raiz (root)
        Department root = null;
        
        for (int i = 0; i < n; i++)
        {
            string type = Console.ReadLine();
            
            if (type == "department")
            {
                string name = Console.ReadLine();
                string parent = Console.ReadLine();
                
                // TODO: Criar o departamento
                // TODO: Adicioná-lo ao dicionário de departamentos
                // TODO: Se parent for "root", este é o departamento raiz
                // TODO: Caso contrário, encontre o departamento pai e adicione este departamento a ele
            }
            else if (type == "employee")
            {
                string name = Console.ReadLine();
                int salary = Convert.ToInt32(Console.ReadLine());
                string parent = Console.ReadLine();
                
                // TODO: Criar o funcionário
                // TODO: Encontrar o departamento pai e adicionar o funcionário a ele
            }
        }
        
        // TODO: Imprimir o salário total do departamento raiz
        // Formato: "{root_name} Total Salary: {amount}"
        Console.WriteLine($"{root.Name} Total Salary: {root.GetSalary()}");
    }
}
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