Seguro que la mayoría de vosotros habéis escuchado alguna vez la palabra SOLID, la Santísima Trinidad de la programación orientada a objetos y es que bajo esta palabra se esconden sus 5 principios básicos: Single responsability, Open-closed, Liskov substitution, Interface segregation and Dependency inversion.

Pero, si no somos SOLID… ¿qué estamos haciendo? Probablemente estés escribiendo código ¡STUPID! Ya te podrás imaginar que también es un acrónimo:

Singleton

Uno de los primeros patrones que aprendemos por su facilidad de entender e implementar e intenta resolver básicamente dos problemas (adiós Principio de responsabilidad única):

  • Garantizar que una clase tenga una única instancia, por ejemplo, para controlar el acceso a un recurso compartido como una base de datos
  • Proporcionar un acceso seguro a estados globales (variables globales)

La misma instancia viajará por todo el código y todos los clientes la compartirán. Incluso la mayoría de ellos no serán conscientes de que están reusando la misma instancia que ha sido utilizada por otros. Parece una fiesta pero definitivamente esto no mola:

  • Proporciona estados globales dentro de la aplicación lo que infiere en dificultar para probar (testing) nuestro código
  • No permite inyección de dependencias
  • Aplicaciones con estados globales son aplicaciones que esconden sus dependencias
  • Nuestros clientes quedan acoplados a los Singleton
  • Incumple la S de SOLID. Se preocupa de solucionar dos problemas: cómo se instancia una clase (ocultando su constructor haciéndolo privado) y cómo se comporta su propio ciclo de vida
  • Si haces DDD estás en apuros. En DDD los conceptos únicos son relativos al contexto. Pero un Singleton es siempre el mismo objeto, le da igual el contexto en el que estés

Tight coupling (strong coupling)

El acoplamiento fuerte es una generalización del patrón Singleton. Básicamente consiste en qué grado tus clientes conocen detalles de las clases que consumen. Entre más detalles conozcan (alto acoplamiento) más difícil será realizar cambios en tu código. También aumentará la probabilidad de romper y mantener tus aplicaciones.

Imagina que queremos conocer si hoy es el cumpleaños de nuestros usuarios:

<?php

class Usuario
{
    public int $dia_cumpleaños;
    public int $mes_cumpleaños;

    public function __construct(...): void
    {
     // TO-DO
    }
 }

 class Cliente
 {
    public function felicitar(Usuario $usuario): void
    {
        $today = getdate();

        if (
            $today['mday'] === $usuario->dia_cumpleaños && 
            $today['mon'] === $usuario->mes_cumpleaños
        ) {
            // mandar email
        }
    }
 }

Tenemos una clase Usuario que almacena la fecha de nacimiento del usuario como dos atributos de clase de tipo entero: un atributo para el día y otro para el mes. Además, por otro lado, hemos implementado una clase que mandará un e-mail de felicitación al usuario el día de su cumpleaños. Para esto miraremos si los atributos de la clase Usuario coinciden con el día y el mes de la fecha de hoy.

El equipo A decide cambiar la forma en la que se almacena la fecha de nacimiento de un usuario. Y nuestra clase Cliente deja de funcionar, se ha roto. ¿Por qué? Porque Cliente está fuertemente acoplado con Usuario: conoce demasiados detalles de su implementación para funcionar. Está violando el principio Tell, Don’t Ask: desde getters y setters hasta clases anémicas.

  • Nuestro código no es tolerante al cambio
  • Dificulta reutilizar código
  • Dificulta probar (testing) nuestros módulos o clases

Untestability

Como hemos visto usar el patrón Singleton y tener código fuertemente acoplado reduce enormemente nuestras posibilidades de probar (testing) código. Y deberíamos partir de una premisa fundamental: ¡nuestro código debe ser fácil de probar! ¿Sabes la tranquilidad que da llegar a producción en verde?

Premature optimization

Premature optimization is the root of all evil. There is only cost, and no benefit.

Donald Knuth

Seamos sinceros, la mayoría de las optimizaciones que hacemos los desarrolladores no sirven para nada. Punto. Miento, sí, para una única cosa muy diferente a su propósito: dificultar la legibilidad de nuestro código.

Las técnicas de optimización son mucho más complejas que sustituir un loop de 20 ítems que usa Active Record por una query hecha a pelo. Y se consideran un anti patrón.

Por Internet corren las siguientes voces sobre la optimización prematura:

  • No lo hagas
  • No lo hagas todavía

Indescriptive naming

Este parece obvio, ¿quién iba a tirarse piedras sobre su mismo tejado? Pues lo hacemos todos y todos los días. Sobretodo evita las abreviaturas. ¿A qué debemos esta manía? Las abreviaturas no aportan ninguna ventaja y muchas veces tiramos de ellas como solución a problemas de indentación de código (¿sabes por qué deberías seguir una guía de estilo de código?)

Además, son peligrosas. Un ejemplo muy típico. tmp y temp ¿esto qué significa? ¿temporal? ¿temperatura? No des por hecho que todo el mundo va utilizar tmp para temporal y temp para temperatura.

Duplication

La duplicación de código supone la violación de varios principios: Don’t Repeat Yourself (DRY) y Keep it simple, stupid (KISS).

Aunque ojo, no en el 100% de los casos es así: si haces DDD podrías ser bueno tener objetos repetidos en contextos diferentes; y a veces es mejor un poco de código repetido que una mala abstracción. Lo veremos en futuras entradas con más detalle.

Y ahora que conoces todas las desventajas de escribir STUPID, ¿te animas a escribir SOLID?