Menu
Coddy logo textTech

Patrón Composite

Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy. Lección 61 de 70.

El patrón Composite es un patrón estructural que te permite componer objetos en estructuras de árbol y tratar de manera uniforme los objetos individuales y los grupos de objetos. Es perfecto para representar jerarquías de partes y conjuntos en las que quieres trabajar tanto con elementos individuales como con colecciones mediante la misma interfaz.

El patrón tiene tres componentes clave: una interfaz de componente que define operaciones comunes, clases hoja que representan objetos individuales y clases compuestas que contienen hijos e implementan la misma interfaz:

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());
}

La belleza de este patrón es que los clientes no necesitan saber si están trabajando con un solo archivo o con una jerarquía de carpetas completa:

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

El patrón Composite es ideal para sistemas de archivos, árboles de componentes de UI, organigramas o cualquier estructura en la que los contenedores y los contenidos deban tratarse de la misma manera. Simplifica el código cliente al eliminar la necesidad de distinguir entre elementos simples y complejos.

challenge icon

Desafío

Fácil

Construyamos un sistema de organigrama usando el patrón Composite. Crearás una estructura en la que tanto los empleados individuales como los departamentos (que contienen empleados u otros departamentos) puedan tratarse de manera uniforme, lo que te permitirá calcular el costo salarial total en cualquier nivel de la jerarquía.

Organizarás tu código en tres archivos:

  • OrgComponent.cs: Define una interfaz IOrgComponent en el espacio de nombres Organization con dos miembros: una propiedad Name (string) y un método GetSalary() que devuelve un entero. Esta interfaz representa el contrato común que seguirán tanto los empleados individuales como los departamentos.
  • OrgClasses.cs: Crea dos clases en el mismo espacio de nombres:
    • Employee: una clase hoja que representa a un trabajador individual. Recibe un nombre y un salario en su constructor e implementa la interfaz devolviendo su propio salario.
    • Department: una clase compuesta que representa un grupo. Recibe un nombre en su constructor y mantiene una lista de elementos IOrgComponent. Incluye un método Add(IOrgComponent component) para agregar miembros. Su método GetSalary() debe devolver la suma de los salarios de todos sus miembros.
  • Program.cs: Construye una estructura organizativa a partir de la entrada y calcula el salario total. Crearás departamentos y empleados, los anidarás adecuadamente y demostrarás cómo el patrón Composite permite tratar toda la jerarquía de manera uniforme.

Recibirás las siguientes entradas:

  • La cantidad de componentes que se crearán
  • Para cada componente: el tipo (employee o department), seguido del nombre y, para los empleados, su salario. Para los departamentos, también recibirás el nombre del departamento principal (o root si es un departamento de nivel superior)

Formato de entrada para cada componente:

employee
{name}
{salary}
{parent_department}

department
{name}
{parent_department}

Después de construir la estructura, imprime el salario total del departamento raíz con el formato {root_name} Total Salary: {amount}.

Por ejemplo, si las entradas son:

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

La salida debería ser:

Engineering Total Salary: 13500

El departamento Engineering contiene a Alice (5000), Bob (4500) y el departamento QA. QA contiene a Charlie (4000). Cuando llamas a GetSalary() en Engineering, calcula recursivamente el total de todos los componentes anidados, lo que demuestra cómo el patrón Composite permite trabajar con estructuras de árbol complejas mediante una interfaz sencilla y uniforme.

Pruébalo tú mismo

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

class Program
{
    public static void Main(string[] args)
    {
        int n = Convert.ToInt32(Console.ReadLine());
        
        // TODO: Crear un diccionario para almacenar departamentos por nombre
        // Esto te ayudará a encontrar los departamentos padre al añadir componentes
        Dictionary<string, Department> departments = new Dictionary<string, Department>();
        
        // TODO: Mantener el seguimiento del departamento raíz (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: Crear el departamento
                // TODO: Añadirlo al diccionario de departamentos
                // TODO: Si parent es "root", este es el departamento raíz
                // TODO: De lo contrario, encontrar el departamento padre y añadirle este departamento
            }
            else if (type == "employee")
            {
                string name = Console.ReadLine();
                int salary = Convert.ToInt32(Console.ReadLine());
                string parent = Console.ReadLine();
                
                // TODO: Crear el empleado
                // TODO: Encontrar el departamento padre y añadirle el empleado
            }
        }
        
        // TODO: Imprimir el salario total del departamento raíz
        // Formato: "{root_name} Total Salary: {amount}"
        Console.WriteLine($"{root.Name} Total Salary: {root.GetSalary()}");
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de C# online