Object Calisthenics – Colecciones como clases de primer orden.
En esta entrada vamos a ver la cuarta regla de “Colecciones como clases de primer orden” o en inglés “First class collections“, y es bastante sencilla, la verdad, espero acertar con el ejemplo siguiendo con los Robots.
Esta regla dice literalmente así:
Cualquier clase que contenga una colección no debe contener ninguna otra variable miembro.
Y así es como debería ser, vamos a ver un ejemplo y después como siempre refactorizamos y vemos como se queda el código y por qué.
Como hemos dicho, sigamos con la clase Robot, esta vez, vamos a imaginar que tenemos que realizar una nueva funcionalidad en nuestra aplicación, en la cual se va a implementar que el Robot tenga una serie de poderes, técnicamente hablando, una lista de poderes, sean cuales sean, que puede volar, hacerse invisible, da igual, ya que dependiendo del poder que tenga no vamos de momento a implementar ninguna nueva funcionalidad en este mismo momento. Para este ejemplo, vamos a suponer también que cada nueva instancia de Robot va a tener un nombre asignado.
Pues procedemos a ello:
public class Robot
{
public string Name { get; set; }
public List<string> Poderes { get; set; }
private int Position = 0;
public int Avanzar(Pasos pasos)
{
return Position += pasos.Avances;
}
public int Retroceder(Pasos pasos)
{
return Position -= pasos.Avances;
}
}
Lenguaje del código: PHP (php)
Pues ya tendríamos nuestra clase Robot(), la que podríamos instanciar con un Nombre y una lista de Poderes de esta forma:
var robot = new Robot();
robot.Name = "Pepito Grillo";
robot.Poderes = new List<string> { "volar", "invisibilidad" };
var avances = Pasos.Create(10);
robot.Avanzar(avances);
Lenguaje del código: PHP (php)
Ya hemos instanciado nuestro Robot, con un nombre, unos poderes, y hemos avanzado 10 pasos con él, esto es correcto, como mas de una vez he comentado es funcional, aunque si nos ceñimos a la regla que hemos comentado arriba de “cualquier clase que contenga una colección no debe contener ninguna otra variable miembro.“, podemos denotar que ahora la clase Robot contiene una colección de strings llamada Poderes y además contiene Position y Name, por lo que no estaríamos cumpliendo esta regla tal y como indica.
Esto no quiere decir que siempre tengamos que ceñirnos al 100% y cumplirla siempre, aunque yo aconsejo que en la medida de lo posible si la cumplamos. Vamos a ver como sería el código refactorizado y el por qué de realizarlo de esta forma:
public class Poderes
{
public List<string> values;
public Poderes()
{
values = new List<string>();
}
public void Add(string value)
{
values.Add(value);
}
}
public class Robot
{
public string Name { get; set; }
public Poderes Poderes { get; set; }
private int Position = 0;
public Robot() => Poderes = new Poderes();
public int Avanzar(Pasos pasos)
{
return Position += pasos.Avances;
}
public int Retroceder(Pasos pasos)
{
return Position -= pasos.Avances;
}
}
Lenguaje del código: PHP (php)
Con esta refactorización, ya cumpliríamos la regla número 4 de Object Calisthenics, pues tenemos la clase Robot, que no contiene propiedad alguna que sea una colección, y por otro lado tenemos la clase Poderes, que se usa en Robot, que contiene una sola propiedad de tipo colección, por lo que ahora si estaríamos dentro del standar de Object Calisthenics.
¿ Para que hemos hecho esto ?
Pues para que cada colección esté envuelta en su propia clase, y podamos aplicar validaciones dentro de su propia clase, como comentábamos en la anterior entrada, para que ahora sea la clase Poderes, la que tenga la responsabilidad de validar Poderes, o por ejemplo pueda implementar métodos de filtrado de Poderes, hacer uniones de listas de Poderes… y un largo etcétera.
Todas estas validaciones y funcionalidades quedarán encapsuladas en su misma clase, ya que es la que tiene la responsabilidad de si misma, y no debe delegarla en nadie mas.
Con esto conseguimos tener el código mas encapsulado, mas centralizado, y como he comentado en más de una ocasión, vamos a evitar un Code Smell llamado Shotgun Surgery, porque si Poderes no tuviese la responsabilidad de validar, o “jugar” como quiera con su colección, deberíamos ir implementado esto una y otra vez por el todo el código dejando la misma lógica desperdigada por todos lados.
Cuando una misma lógica está repartida con código repetido por toda una aplicación, cuando cambia esa lógica, no tenemos un único sitio donde modificarla, y muy probablemente se nos escape cambiar algo y generemos una funcionalidad errónea en un caso o mas concreto.
Antes de despedirme, me gustaría comentar que en muchas ocasiones mis ejemplos no están refactorizados todo lo que se pudiera, por dos factores, primero para que se puedan entender mejor, y segundo porque me sirven para futuras entradas el ir refactorizándolos a medida que voy explicando cada una de las siguientes reglas.