Código limpio: Estructuras vs objetos

Código limpio: Estructuras vs objetos

Esta entrada está disponible en video

Puedes encontrar una versión en video de esta entrada en nuestro canal de youtube.

Código Limpio

El libro Clean Code o Código Limpo es considerado por muchos como "la biblia" del desarrollo de software. Probablemente esta es una afirmación exagerada, sin embargo no se puede negar que es un gran libro que merece la pena leer. En esta serie de entradas analizamos los principales capítulos del libro clean code publicado por Robert C Martin en el año 2008.

Se han escrito mil entradas sobre el que se considera uno de los más importantes libros sobre filosofía de desarrollo de software, pero nosotros en esta serie de entradas queremos profundizar un poco más en cada uno de los capítulos. Si te dedicas a programar y te interesa mejorar en tu profesión quedate. A lo largo de esta serie de entradas vamos a aprender un montón de cosas.


Si lo deseas puedes ver el resto de entradas de la serie: Código Limpio

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. 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.