Código limpio: Estructuras vs objetos

Código limpio: Estructuras vs objetos

En esta entrada vamos a ver que son las estructuras de datos y los objetos, y que ventajas puede tener el uso de uno u otro tipo.

Esta es la séptima entrega de una serie en la que analizamos el libro clean code de Robert C Martin publicado en el año 2008, Si quieres puedes ver el índice de la serie. También puedes ver la versión en video de esta entrada en nuestro canal de youtube.

Vamos a implementar exactamente lo mismo con las dos aproximaciones para ver cómo se comporta cada una.

Estructuras de datos

Una estructura de de datos, es una clase o un objeto que expone la información que contiene. En el ejemplo hemos evitado el uso de getters y setter, pero sería exactamente lo mismo si los tuviera, lo consideraremos una estructura, porque expone su información. Otras clases serán responsables de realizar las funcionalidad que necesitemos.

class Circle
{
    /** @var Point */
    public $center;
    public $radius;
}

class Geometry
{
    const PI = 3.141592653589793;

    public function area($shape): float
    {
        return $shape->radius * $shape->radius * Geometry::PI;
    }
}

Objetos

Con el mismo ejemplo si usaramos el paradigma de la orientación a objetos, debemos ocultar cuál es la información y la estructura de los datos internos, y debemos exponer la funcionalidad que queremos.

class Circle
{
    const PI = 3.141592653589793;

    /** @var Point */
    private $center;
    private $radius;

    public function area(): float
    {
        return $this->radius * $this->radius * self::PI;
    }
}

Aunque el ejemplo es muy sencillo nos permite entender la diferencia. Una estructura de datos es un elemento sencillo que solamente contiene información, mientras que la lógica se encuentra en otras clases. Un objeto es una abstracción que expone su funcionalidad.

Añadir nuevas estructuras de datos / objetos

Vamos añadir un nuevo tipo: Rectangle y vemos a ver que cambios son necesarios en cada aproximación.

class Rectangle
{
    /** @var Point */
    public $topLeft;
    public $height;
    public $width;
}

class Circle
{
    /** @var Point */
    public $center;
    public $radius;
}

class Geometry
{
    const PI = 3.141592653589793;

    public function area($shape): float
    {
        if ($shape instanceof Rectangle) {
            return $shape->height * $shape->width;
        }

        if ($shape instanceof Circle) {
            return $shape->radius * $shape->radius * Geometry::PI;
        }

        throw new InvalidArgumentException();
    }
}
Estructura de datos
class Rectangle
{
    /** @var Point */
    private $topLeft;
    private $height;
    private $width;

    public function area(): float
    {
        return $this->height * $this->width;
    }
}

class Circle
{
    const PI = 3.141592653589793;

    /** @var Point */
    private $center;
    private $radius;

    public function area(): float
    {
        return $this->radius * $this->radius * self::PI;
    }
}





    
Orientado a objetos

¿Que diferencia ves entre ambos?. En la primera aproximación además de añadir la nueva estructura, hemos tenido que modificar Geometry::area(). En el ejemplo es un cambio muy sencillo, pero nos sirve para identificar que cuando añadimos nuevas estructuras vamos a tener que revisar el código que las utiliza para que sea capaz de manejar esta nueva estrucutra, lo cual nos puede llevar a añadir errores en un códiugo que ya estaba funcionando.

En cambio en la aproximación orientada a objetos no hemos tenido que modificar ni una coma del código que ya teníamos, y simplemente hemos tenido que añadir un nuevo objeto.

Añadir funcionalidad

Vamos a añadir una nueva funcionalidad, el cálculo del perímetro: perimeter a ver que tal se comporta cada una de nuestras implementaciones.

class Rectangle
{
    /** @var Point */
    public $topLeft;
    public $height;
    public $width;
}

class Circle
{
    /** @var Point */
    public $center;
    public $radius;
}

class Geometry
{
    const PI = 3.141592653589793;

    public function area($shape): float
    {
        if ($shape instanceof Rectangle) {
            return $shape->height * $shape->width;
        }

        if ($shape instanceof Circle) {
            return $shape->radius * $shape->radius * Geometry::PI;
        }

        throw new InvalidArgumentException();
    }

    public function perimeter($shape): float
    {
        if ($shape instanceof Rectangle) {
            return 2 * $shape->height + 2 * $shape->width;
        }

        if ($shape instanceof Circle) {
            return 2 * Geometry::PI * $shape->radius;
        }

         throw new InvalidArgumentException();
    }
}
Estructuras de datos
class Rectangle
{
    /** @var Point */
    private $topLeft;
    private $height;
    private $width;

    public function area(): float
    {
        return $this->height * $this->width;
    }

    public function perimeter(): float
    {
        return 2 * $this->height + 2 * $this->width;
    }
}

class Circle
{
    const PI = 3.141592653589793;

    /** @var Point */
    private $center;
    private $radius;

    public function area(): float
    {
        return $this->radius * $this->radius * self::PI;
    }

    public function perimeter(): float
    {
        return 2 * self::PI * $this->radius;
    }
}








    
Orientado a objetos

En este caso, nuestra primera aproximación ha sido más sencilla. No hemos tenido que tocar el código que ya teníamos funcionando y sólo hemos tenido que añadir una nueva función.

En cambio en la aproximación orientada a objetos hemos tenido que tocar cada una de las clases que teníamos para añadirles esta funcionalidad. El ejemplo es muy sencillo, pero nos sirve para identificar el problema.

Conclusiones

A nivel de líneas de código no existe una diferencia sustancial. Tampoco es razonablemente más sencillo de implementar o de mantener una u otra aproximación, pero cuando utilizamos estructuras, tendremos cierta facilidad cuando queramos añadir nueva funcionalidad, mientras que cuando utilizamos objetos tendremos facilidades para añadir nuevos tipos.

¿Quieres ser una bestia del desarrollo de software?
¡Continúa con nosotros en YouTube!

Todas las semanas un nuevo vídeo sobre desarrollo de software en tu bandeja de entrada.

Tranquilo, no te vamos a enviar spam.