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()); // 650El 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.
Desafío
FácilConstruyamos 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 interfazIOrgComponenten el espacio de nombresOrganizationcon dos miembros: una propiedadName(string) y un métodoGetSalary()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 elementosIOrgComponent. Incluye un métodoAdd(IOrgComponent component)para agregar miembros. Su métodoGetSalary()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 (
employeeodepartment), seguido del nombre y, para los empleados, su salario. Para los departamentos, también recibirás el nombre del departamento principal (orootsi 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
QALa salida debería ser:
Engineering Total Salary: 13500El 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()}");
}
}
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
1Fundamentos de OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantesPractica por tu cuenta: Compilador de C# online