La S de SOLID

Para poder decir que uno escribe código limpio tiene que comprender y saber aplicar lo que esconde el acrónimo S.O.L.I.D. El programador Robert C. Martin lo acuñó con el objeto de recordar estas cinco normas que todo clean coder debe divulgar y utilizar.

Estos principios se aplican a programación orientada a objetos. El objetivo principal es que nuestros programas sean mas fácilmente mantenibles y comprensibles así como permitir que crezcan y evolucionen de forma limpia e inteligente.

Los cinco principios son :

  1. Single responsibility
  2. Open-closed
  3. Liskov substitution
  4. Interface segregation
  5. Dependency inversion

Si queréis escribir buen código, aplicar estos principios. ¿Perdéis la calma luchando contra vuestro código? ¿Vuestros programas están llenos de wtf’s? ¿Vuestro código es hostil? Tal vez necesitáis S.O.L.I.D inyectado en vena.

En este post escribo sobre el primer principio.

Robert C. Martin definió SRP inspirándose en el concepto de cohesión explicado por Tom DeMarco. Cuando he leído sobre SRP la parte mas difícil de comprender ha sido precisamente “responsabilidad”.

¿Qué es una responsabilidad?

Una razón para cambiar

 

¿Y qué es una razón para cambiar?: Una de las explicaciones que mas me gusta es la que atiende al sujeto de esa responsabilidad, es decir: ¿a quien responde esa responsabilidad?

Una clase solo debería responder a una necesidad

 

Si una clase posee varias responsabilidades, estará también articulando varias relaciones de responsabilidad con varios actores.

No podemos entender responsabilidad como algo concreto a nivel técnico, ni asimilarlo al trabajo de una función o de toda una clase. La responsabilidad es algo mas amplio, es una explicación semántica sobre lo que hace la clase, no una definición de las tareas que ejecuta.

Es un error pensar que este principio acarrea clases de una sola función. Una sola responsabilidad puede ser definida por diversas funciones, o por una sola, seria estúpido establecer un numero. Aquí de lo que se trata es de que nuestra clase responda a una sola exigencia, a una solo necesidad. Si mi clase solo responde a una necesidad, entonces solo hay un motivo por que el que pueda ser modificada: el cambio de la necesidad misma.

Imaginemos un programa que necesita utilizar la API de un servicio web. Podemos pensar en construir una clase que realice una llamada al servicio, trate la respuesta y actualice una base de datos con las informaciones recibidas.

class WebServiceConnector
{
    public function makeCall()
    {
    }

    public function parseResponse()
    {
    }

    public function connectDatabase()
    {
    }

    public function updateInfo()
    {
    }
}

Analizando ligeramente esta clase, vemos que en efecto hay muchas razones para cambiar, es decir, que es muy probable que tengamos que modificar esta clase por diferentes motivos.

Imaginemos que usamos una base de datos mysql pero que cuando nuestro sistema crece hasta los trescientos millones de registros, decidimos usar una base de datos hypertable. Deberemos realizar cambios relativos a bases de datos en una clase en la que por ejemplo, encontramos llamadas a servicios web: dos responsabilidades que definitivamente no tienen nada en común.

Si el servicio web que utilizamos cambia su interfaz, haremos cambios en la función que realiza las llamadas, mientras que nuestra base de datos, nuestro parseo de datos y nuestra modificación en base de datos, no necesitan ser modificados.

Esta clase responde a la necesidad de comunicarse con un servicio web, de analizar los documentos XML recibidos, de gestionar la comunicación con la base de datos y también de actualizar informaciones en una base de datos. Es una clase Frankenstein que pretende contentar a muchos actores: documentos XML, servicios web soap o bases de datos mysql.

Todas estas responsabilidades, agrupadas en una sola clase están acopladas. Es decir, tienen un nivel de acoplamiento máximo cuando en realidad son objetivamente distintas.

Es necesario separarlas:

class SoapClient
{

}

class Parser
{

}

class DatabaseDriver
{

}

class ApiConnector
{

}

Aquí no pretendo ser exhaustivo, solo reflejar la necesidad de SRP conceptual o filosóficamente. Por supuesto, nos debemos preguntar si estas clases que hemos creado tras una primera reflexión, tienen una sola responsabilidad, es decir, si tienen un solo motivo para cambiar, y si no, analizarlas y modificarlas convenientemente.

Este principio es la base de todo lo demás. Si el programador no pone empeño y horas en realizar un buen diseño, separando las responsabilidades y evitando el acoplamiento, muchas otras buenas practicas y principios se verán muy comprometidos.