Composite - Patrones de Diseño

A
Antonio Leiva
4 min lectura

El patrón de diseño Composite es una técnica utilizada en programación orientada a objetos para tratar grupos de objetos de manera uniforme, como si fueran un solo objeto.

Este patrón permite a los desarrolladores construir estructuras jerárquicas y manejar los elementos de forma recursiva.

En Kotlin, el patrón Composite se implementa mediante la interface Composable y la clase Composite, que se utilizan para definir la estructura del grupo de objetos y sus operaciones respectivamente.

Estructura de implementación en Kotlin

// Interface que define la operación composable
interface Composable {
    fun operation(): String
}

// Clase que representa un objeto individual del grupo
class IndividualObject: Composable {
    override fun operation() = "Individual object operation"
}

// Clase que representa el grupo de objetos y sus operaciones
class Composite: Composable {
    private val objects = mutableListOf<Composable>()

    fun add(object: Composable) {
        objects.add(object)
    }

    fun remove(object: Composable) {
        objects.remove(object)
    }

    override fun operation() = objects.map { it.operation() }.joinToString(separator = "\n")
}

// Creación del grupo de objetos y su uso
fun main() {
    val composite = Composite()

    composite.add(IndividualObject())
    composite.add(IndividualObject())

    println(composite.operation()) // Imprime la operación de cada objeto del grupo
}

Ventajas del patrón Composite

  • Permite tratar a un grupo de objetos de manera uniforme, lo que facilita su manejo y organización.

  • Permite construir estructuras jerárquicas y manejarlas de manera recursiva.

  • Provee una interfaz común para todos los objetos del grupo, lo que facilita su extensión y modificación.

Desventajas del patrón Composite

  • Puede ser complejo de implementar y difícil de entender para programadores no familiarizados con el patrón.

  • Puede aumentar la complejidad del código y dificultar su depuración.

Ejemplo

Un ejemplo concreto del uso del patrón Composite puede ser el desarrollo de una aplicación de dibujo en la que se puedan crear y manipular diferentes figuras geométricas.

Por ejemplo, se puede implementar una clase abstracta Figure que represente a una figura genérica y tenga métodos para dibujarla, moverla y obtener su área. Luego, se pueden crear clases concretas como Circle, Rectangle y Triangle que hereden de Figure y implementen las operaciones específicas de cada figura.

Para manejar grupos de figuras, se puede utilizar el patrón Composite y crear una clase Group que implemente la interface Composable y permita agregar y eliminar figuras individuales del grupo. De esta manera, se pueden tratar los grupos de figuras de manera uniforme, como si fueran una sola figura, y manejarlos de manera recursiva.

Aquí está un ejemplo de implementación en Kotlin:

// Clase abstracta que representa a una figura genérica
abstract class Figure {
    abstract fun draw()
    abstract fun move(x: Int, y: Int)
    abstract fun area(): Double
}

// Clases concretas que representan figuras específicas
class Circle(val radius: Double): Figure() {
    override fun draw() = println("Dibujando círculo de radio $radius")
    override fun move(x: Int, y: Int) = println("Moviendo círculo a ($x, $y)")
    override fun area() = Math.PI * radius * radius
}

class Rectangle(val width: Double, val height: Double): Figure() {
    override fun draw() = println("Dibujando rectángulo de ancho $width y alto $height")
    override fun move(x: Int, y: Int) = println("Moviendo rectángulo a ($x, $y)")
    override fun area() = width * height
}

class Triangle(val base: Double, val height: Double): Figure() {
    override fun draw() = println("Dibujando triángulo de base $base y altura $height")
    override fun move(x: Int, y: Int) = println("Moviendo triángulo a ($x, $y)")
    override fun area() = base * height / 2
}

// Clase que representa un grupo de figuras y sus operaciones
class Group: Composable {
    private val figures = mutableListOf<Figure>()

    fun add(figure: Figure) {
        figures.add(figure)
    }

    fun remove(figure: Figure) {
        figures.remove(figure)
    }

    override fun operation() = figures.forEach { it.draw() }
}

// Creación de un grupo de figuras y su uso
fun main() {
    val group = Group()

    group.add(Circle(5.0))
    group.add(Rectangle(10.0, 5.0))
    group.add(Triangle(10.0, 5.0))

    group.operation() // Dibuja cada figura del grupo
}

En este caso, se crea un grupo de figuras y se agrega un círculo, un rectángulo y un triángulo. Luego, se llama al método operation() del grupo, que dibuja cada una de las figuras agregadas.

De esta manera, se puede tratar el grupo de figuras como si fuera una sola figura y manejarlo de manera uniforme. También se pueden crear grupos de grupos y manejarlos de manera recursiva.

Conclusión

En conclusión, el patrón Composite es una técnica útil en programación orientada a objetos para tratar grupos de objetos de manera uniforme.

Su correcta implementación puede mejorar la organización y manejo de estructuras jerárquicas en el código.

Sin embargo, es importante tener en cuenta sus desventajas y utilizarlo de manera adecuada en cada caso.