Código limpio: No hables con extraños
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 la última entrada repasamos qué son las estructuras de datos, en que se diferencian de los objetos y cuándo conviene más utilizar una aproximación u otra. En esta entrada vamos a explicar cual es la ley de demeter, que problemas puedes tener si es que no la cumples y que opciones tienes para cumplirla.
Empezemos describiendo los motivos por los que no deberías hablar con extraños: muchas veces cuando tenemos una clase que a su vez está formada por objetos de otras clases y estas a su vez por otras, tenemos una estructura de clases. Hasta aquí todo bien, pero: ¿qué ocurre cuando estas estructuras se vuelven complejas? Veamos un ejemplo que podría darse en un comando de symfony.
class CustomCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->getApplication()->getKernel()->getContainer()->getCustomService()->getManager()->execute();
return 1;
}
}
Como puedes ver, estamos acoplando de forma innecesaria nuestro código a todas las clases que aparecen en la cadena de llamadas. Cualquier cambio en estas clases o en las relaciones entre ellas podría suponer que nuestro código dejase de funcionar. Para que esto no suceda viene al rescate la ley de demeter.
La ley de demeter
La ley de demeter fue descrita por primera vez dentro del proyecto demeter en 1987. Como curiosidad quiero contarte que Demeter, hace referencia a la diosa griega de la agricultura. Querian hacer referencia a algo así como "cultivar" software en contraposición a "construir" el software.
La ley indica que desde un método solo deberías "hablar" con aquellos a quien conozcas bien. Y estos son:
- Métodos de la misma clase.
- Métodos de los objetos de la clase.
- Métodos de los objetos creados en la misma función.
- Métodos de los objetos pasados como argumento a la función.
¿Y cómo lo soluciono?
Ya hemos detectado que es lo que no debes hacer, te voy a proponer ahora tres opciones a tener en cuenta cuando detectes este problema en tu código.
No hacer nada
La primera es no hacer nada. Es la solución más sencilla, y la peor de las tres que vamos a ver, pero como ya vimos en la entrada sobre los dogmas en el desarrollo de software, si crees que las clases que estás llamando y sus relaciones están muy consolidadas, podrías permitirte el lujo de realizar este tipo de llamadas en cadena.
Usar atajos
La segunda consiste en hacer atajos que permitan evitar estas llamadas encadenadas. Esta es una solución bastante rápida, que te puede permitir salir del paso en algún momento.
Por ejemplo en CustomService
podríamos añadir el método executeManager
.
class CustomService
{
public function executeManager(): void
{
$this->getManager()->execute();
}
}
Esta solución como puedes imaginarte tampoco es la mejor. Te permite abstraer parte de esa cadena de llamadas y por lo tanto es una opción mejor que al principio, ya que si la relacion cambia sólo tendrás que cambiar en este punto las llamadas.
El problema de esta solución es que puedes encontrarte con tu código con un montón de estos métodos atajos, que realmente por diseño no deberían encontraser ahí, pero están simplemente para hacer cumplir la ley de demeter.
Inyectar dependencias
La solución buena, como probablemente ya te venías imaginando es inyectar las dependencias. Veamos un ejemplo.
class CustomCommand extends Command
{
public function setCustomManager(CustomManager $customManager)
{
$this->manager = $customManager;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->manager->execute();
}
}
Hemos añadido como dependencia la clase CustomManager
que era la que realmente necesitamos, y se la hemos inyectado a través de un setter, pero hemos eliminado toda la cadena de dependencias y las relaciones entre sí.
Con este sencillo cambio hemos podido eliminar fácilmente todas las dependencias sobrantes.