Menu
Coddy logo textTech

Patrón Composite

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 96 de 107.

El patrón Composite te permite tratar objetos individuales y grupos de objetos de manera uniforme. Mientras que State cambia el comportamiento según el estado interno, Composite construye estructuras de árbol en las que tanto las hojas como los contenedores comparten la misma interfaz.

Este patrón es perfecto para estructuras jerárquicas, como sistemas de archivos, organigramas o componentes de UI. En Go, definimos una interfaz común que implementan tanto los elementos individuales como los contenedores:

type Component interface {
    GetSize() int
}

type File struct {
    Name string
    Size int
}

func (f File) GetSize() int {
    return f.Size
}

El compuesto (contenedor) contiene elementos secundarios e implementa la misma interfaz al agregar sus resultados:

type Folder struct {
    Name     string
    Children []Component
}

func (f *Folder) Add(c Component) {
    f.Children = append(f.Children, c)
}

func (f Folder) GetSize() int {
    total := 0
    for _, child := range f.Children {
        total += child.GetSize()
    }
    return total
}

Ahora puedes anidar carpetas dentro de carpetas, y llamar a GetSize() funciona de forma idéntica tanto si se trata de un archivo individual como de un árbol de directorios completo:

docs := &Folder{Name: "docs"}
docs.Add(File{Name: "readme.txt", Size: 100})
docs.Add(File{Name: "notes.txt", Size: 50})

root := &Folder{Name: "root"}
root.Add(docs)
root.Add(File{Name: "config.json", Size: 25})

fmt.Println(root.GetSize())  // 175

Composite es ideal cuando necesitas representar jerarquías de partes y conjuntos, y quieres que los clientes traten los objetos individuales y las composiciones de forma idéntica.

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 forma uniforme, lo que resulta perfecto para calcular los salarios totales de cualquier parte de la organización.

Organizarás tu código en tres archivos:

  • component.go: Define la interfaz común que implementarán tanto los individuos como los grupos.

    Crea una interfaz OrgComponent con dos métodos:

    • GetSalary() int: devuelve el salario total de este componente
    • GetName() string: devuelve el nombre del empleado o departamento
  • organization.go: Implementa los tipos hoja (empleado individual) y compuesto (departamento).

    Crea una estructura Employee con campos Name (string) y Salary (int). Su método GetSalary() devuelve su propio salario, y GetName() devuelve su nombre.

    Crea una estructura Department con un campo Name (string) y un slice Members que contiene elementos OrgComponent. Añade un método Add(c OrgComponent) para añadir miembros. Su método GetSalary() debe devolver la suma de los salarios de todos los miembros, y GetName() devuelve el nombre del departamento.

  • main.go: Construye una estructura organizativa y calcula los salarios.

    Lee el número de empleados. Para cada empleado, lee su nombre y salario, creando instancias de Employee. Después, lee el número de departamentos. Para cada departamento, lee su nombre y la cantidad de índices de miembros, seguidos de esos índices (basados en 0, y referidos a los empleados o departamentos creados anteriormente, en orden de creación).

    Después de construir la estructura, lee un índice final e imprime el nombre y el salario total de ese componente con el formato: [name]: [salary]

Se proporcionarán las siguientes entradas:

  • Número de empleados, seguido del nombre y el salario de cada empleado en líneas separadas
  • Número de departamentos, seguido del nombre de cada departamento, la cantidad de miembros y los índices de los miembros
  • Índice final que se consultará

Por ejemplo, dada la siguiente entrada:

3
Alice
50000
Bob
60000
Carol
55000
1
Engineering
3
0
1
2
3

Tu salida debería ser:

Engineering: 165000

Y dada la siguiente entrada:

4
Alice
50000
Bob
60000
Carol
55000
Dave
70000
2
Dev
2
0
1
QA
2
2
3
4

Tu salida debería ser:

QA: 125000

Y dada la siguiente entrada:

2
Alice
50000
Bob
60000
0
1

Tu salida debería ser:

Bob: 60000

Observa cómo GetSalary() funciona de forma idéntica tanto si lo llamas en un solo empleado como en un departamento completo: ¡el patrón Composite te permite tratar a individuos y grupos de forma uniforme mediante la misma interfaz!

Pruébalo tú mismo

package main

import "fmt"

func main() {
	// Lee el número de empleados
	var numEmployees int
	fmt.Scanln(&numEmployees)

	// Almacena todos los componentes (empleados y departamentos) en orden de creación
	var components []OrgComponent

	// TODO: Lee el nombre y el salario de cada empleado
	// Crea instancias de Employee y añádelas al slice components
	for i := 0; i < numEmployees; i++ {
		var name string
		var salary int
		fmt.Scanln(&name)
		fmt.Scanln(&salary)
		// TODO: Crea el empleado y añádelo a components
	}

	// Lee el número de departamentos
	var numDepartments int
	fmt.Scanln(&numDepartments)

	// TODO: Lee el nombre de cada departamento, la cantidad de miembros y los índices de los miembros
	// Crea instancias de Department, añade los miembros por índice y añádelas a components
	for i := 0; i < numDepartments; i++ {
		var deptName string
		var memberCount int
		fmt.Scanln(&deptName)
		fmt.Scanln(&memberCount)
		// TODO: Crea el departamento, lee los índices de los miembros, añade los miembros, añádelo a components
	}

	// Lee el índice final a consultar
	var queryIndex int
	fmt.Scanln(&queryIndex)

	// TODO: Imprime el nombre del componente y el salario total en el formato: [name]: [salary]
	// Ejemplo: fmt.Printf("%s: %d\n", name, salary)
}
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 Go online