Introducción

El patrón Composite es un patrón estructural que permite tratar objetos individuales y composiciones de objetos de manera uniforme. Este patrón es útil cuando necesitas trabajar con estructuras jerárquicas, como árboles, donde los nodos pueden ser tanto objetos simples como compuestos.

Objetivos del Patrón Composite

  • Uniformidad: Permitir que los clientes traten de manera uniforme objetos individuales y compuestos.
  • Recursividad: Facilitar la definición de estructuras jerárquicas recursivas.
  • Flexibilidad: Simplificar el código cliente al permitir que interactúe con objetos individuales y compuestos de la misma manera.

Estructura del Patrón Composite

El patrón Composite se compone de los siguientes elementos:

  1. Component: Define la interfaz para los objetos en la composición.
  2. Leaf: Representa los objetos individuales en la composición.
  3. Composite: Representa los objetos compuestos que pueden contener otros objetos (tanto hojas como otros compuestos).

Diagrama UML

  +-------------------+
  |     Component     |
  +-------------------+
  | +operation()      |
  +-------------------+
          ^
          |
  +-------+-------+
  |               |
+------+       +---------+
|  Leaf |       | Composite|
+------+       +---------+
| +operation() | +operation() |
+------+       | +add(Component) |
               | +remove(Component) |
               | +getChild(int) |
               +---------+

Ejemplo Práctico

Vamos a implementar un ejemplo en Python para ilustrar cómo funciona el patrón Composite. En este ejemplo, crearemos una estructura jerárquica de archivos y carpetas.

Código

from abc import ABC, abstractmethod

# Component
class FileSystemComponent(ABC):
    @abstractmethod
    def show_details(self):
        pass

# Leaf
class File(FileSystemComponent):
    def __init__(self, name):
        self.name = name

    def show_details(self):
        print(f"File: {self.name}")

# Composite
class Directory(FileSystemComponent):
    def __init__(self, name):
        self.name = name
        self.children = []

    def add(self, component):
        self.children.append(component)

    def remove(self, component):
        self.children.remove(component)

    def show_details(self):
        print(f"Directory: {self.name}")
        for child in self.children:
            child.show_details()

# Uso del patrón Composite
if __name__ == "__main__":
    file1 = File("file1.txt")
    file2 = File("file2.txt")
    file3 = File("file3.txt")

    dir1 = Directory("dir1")
    dir2 = Directory("dir2")

    dir1.add(file1)
    dir1.add(file2)

    dir2.add(file3)
    dir2.add(dir1)

    dir2.show_details()

Explicación del Código

  1. FileSystemComponent: Es la clase abstracta que define la interfaz común para los objetos en la composición.
  2. File: Es la clase que representa los objetos individuales (hojas) en la composición.
  3. Directory: Es la clase que representa los objetos compuestos (directorios) que pueden contener otros objetos.

En el bloque de uso del patrón Composite, creamos archivos y directorios, y luego los organizamos en una estructura jerárquica. Finalmente, llamamos al método show_details en el directorio raíz para mostrar la estructura completa.

Ejercicio Práctico

Ejercicio

Implementa el patrón Composite para una estructura de menús y submenús en un sistema de navegación de una aplicación.

  1. Crea una clase abstracta MenuComponent con un método display.
  2. Implementa una clase MenuItem que represente un elemento de menú individual.
  3. Implementa una clase Menu que represente un menú que puede contener otros menús y elementos de menú.
  4. Crea una estructura de menús y submenús y muestra la estructura completa.

Solución

from abc import ABC, abstractmethod

# Component
class MenuComponent(ABC):
    @abstractmethod
    def display(self):
        pass

# Leaf
class MenuItem(MenuComponent):
    def __init__(self, name):
        self.name = name

    def display(self):
        print(f"MenuItem: {self.name}")

# Composite
class Menu(MenuComponent):
    def __init__(self, name):
        self.name = name
        self.children = []

    def add(self, component):
        self.children.append(component)

    def remove(self, component):
        self.children.remove(component)

    def display(self):
        print(f"Menu: {self.name}")
        for child in self.children:
            child.display()

# Uso del patrón Composite
if __name__ == "__main__":
    item1 = MenuItem("Home")
    item2 = MenuItem("About")
    item3 = MenuItem("Contact")

    submenu = Menu("Services")
    submenu.add(MenuItem("Consulting"))
    submenu.add(MenuItem("Support"))

    main_menu = Menu("Main Menu")
    main_menu.add(item1)
    main_menu.add(item2)
    main_menu.add(item3)
    main_menu.add(submenu)

    main_menu.display()

Explicación de la Solución

  1. MenuComponent: Es la clase abstracta que define la interfaz común para los objetos en la composición.
  2. MenuItem: Es la clase que representa los elementos de menú individuales.
  3. Menu: Es la clase que representa los menús que pueden contener otros menús y elementos de menú.

En el bloque de uso del patrón Composite, creamos elementos de menú y menús, y luego los organizamos en una estructura jerárquica. Finalmente, llamamos al método display en el menú principal para mostrar la estructura completa.

Conclusión

El patrón Composite es una poderosa herramienta para manejar estructuras jerárquicas de manera uniforme. Permite tratar objetos individuales y compuestos de la misma manera, simplificando el código cliente y facilitando la definición de estructuras recursivas. Con la práctica y la implementación de ejemplos como los presentados, podrás dominar este patrón y aplicarlo eficazmente en tus proyectos de software.

© Copyright 2024. Todos los derechos reservados