1. ¿Qué es el patrón compuesto?

El Patrón Compuesto es un patrón de diseño estructural de objetos comúnmente utilizado. Combina objetos en una estructura de árbol para representar una relación jerárquica de "todo-parte", lo que permite a los clientes manejar objetos individuales y composiciones de objetos de manera consistente.

2. Características y ventajas del patrón compuesto

Las principales ventajas del patrón compuesto son:

  1. Puede definir claramente objetos complejos jerárquicos, representando toda o parte de la jerarquía de objetos, lo que facilita la adición de nuevos componentes.
  2. Proporciona una interfaz unificada, haciendo que el acceso a las partes compuestas y a los objetos individuales sea consistente, permitiendo a los clientes utilizar de manera uniforme objetos individuales y compuestos.

3. Escenarios de aplicación del patrón compuesto

  1. Cuando se desea representar la jerarquía de "todo-parte" de los objetos.
  2. Cuando se desea que los clientes ignoren las diferencias entre los objetos compuestos y los objetos individuales, y utilicen de manera uniforme todos los objetos en la estructura compuesta.

4. Implementación del patrón compuesto en Golang

Supongamos que estamos diseñando una aplicación de comercio electrónico, el catálogo de productos es un buen ejemplo del patrón compuesto. Una categoría puede contener otras categorías, y también productos (por ejemplo, la categoría de electrónicos contiene teléfonos, computadoras, y los teléfonos contienen iPhones, Samsung phones, etc.).

4.1 Diagrama de clases UML

Patrón Compuesto en Golang

4.2 Introducción del ejemplo

En este ejemplo, tenemos Componente (obviamente utilizando la característica de interfaz de la orientación a objetos) como la clase base abstracta, y Compuesto y Hoja implementan esta interfaz, representando objetos contenedores y objetos básicos, respectivamente.

4.3 Paso 1 de Implementación: Definir la clase de componente abstracto

4.3.1 Definir la interfaz de la clase de componente abstracto

// Componente: Interfaz del componente base, define la similitud entre grupos e individuos
type Componente interface {
    Buscar(string)
}

4.3.2 Implementar métodos básicos de la clase de componente abstracto

Este paso se implementa específicamente en la clase de componente contenedor y la clase de componente hoja.

4.4 Paso 2 de Implementación: Definir la clase de componente hoja

Esta clase concreta representa la categoría o el objeto inferior en la jerarquía, y no tiene la siguiente capa de objetos.

4.4.1 Heredar de la clase de componente abstracto

En Go, la herencia de interfaces se implementa mediante la forma en que los métodos se implementan a través de Struct.

4.4.2 Implementar métodos específicos de la clase de componente hoja

// Producto: Representa un nodo hoja, es decir, un producto, y no puede tener nodos secundarios
type Producto struct {
    Nombre string
}

// Buscar: Buscar productos
func (p *Producto) Buscar(palabraClave string) {
    if strings.Contains(p.Nombre, palabraClave) {
        fmt.Printf("Producto: '%s' contiene la palabra clave: '%s'\n", p.Nombre, palabraClave)
    }
}

4.5 Paso 3 de Implementación: Definir la clase de componente contenedor

Esta clase se utiliza para almacenar y gestionar objetos secundarios, generalmente incluyendo algunos métodos para gestionar y organizar objetos secundarios, como agregar(Componente), eliminar(Componente), etc.

4.5.1 Heredar de la clase de componente abstracto

Esto también se implementa utilizando Struct para implementar los métodos de la interfaz en Go.

4.5.2 Implementar métodos específicos de la clase de componente contenedor

// Categoría: Representa un nodo contenedor, es decir, una categoría de productos, que puede tener nodos secundarios
type Categoria struct {
    Nombre    string
    Hijos []Componente
}

// Agregar: Agregar un nodo secundario
func (c *Categoria) Agregar(hijo Componente) {
    c.Hijos = append(c.Hijos, hijo)
}

// Eliminar: Eliminar un nodo secundario
func (c *Categoria) Eliminar(hijo Componente) {
    // Implementación específica omitida
}

// Buscar: Buscar productos
func (c *Categoria) Buscar(palabraClave string) {
    fmt.Printf("Categoría: %s\n", c.Nombre)
    for _, compuesto := range c.Hijos {
        compuesto.Buscar(palabraClave)
    }
}

4.6 Implementación Paso 4: Ejemplo de Código del Cliente

Crear una estructura, ensamblarla en una estructura de árbol y luego llamar a la operación de acceso de la estructura de árbol.

func main() {
    root := &Category{Name: "Raíz"}
    electronics := &Category{Nombre: "Electrónicos"}

    teléfono := &Product{Nombre: "Teléfono"}
    tv := &Product{Nombre: "Televisión"}

    root.Add(electronics)
    electronics.Add(telefono)
    electronics.Add(tv)

    root.Buscar("teléfono") // Esto buscará en todos los hijos
}