Si eres programador, seguramente hayas visto alguna vez esta frase, atribuida a John F. Woods:
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live
Lo que viene a pedir es que escribas tu código con suficiente cuidado para no enfadar a tu compañero de trabajo por si acaso éste es un psicópata asesino.
Aunque comprendo el trasfondo y el motivo de crear una frase tan llamativa, creo que se merece matizarla, ya que incide en el qué pero no en el porqué. Yo interpreto que el mensaje importante que subyace en dicha frase es el siguiente:
Escribe tu código teniendo en cuenta que quien lo lea no tendrá el conocimiento profundo del problema que tú tienes al escribirlo
Escribe código fácil de leer
Como programadores, pasamos mucho más tiempo leyendo código que escribiéndolo (en un ratio de 10 a 1). Por tanto, tenemos que esforzarnos en mejorar ese ratio que lastra nuestra productividad.
Al final, casi cualquier código se acaba leyendo, ya sea por que se tenga que añadir una nueva funcionalidad o corregir un bug. De hecho, es muy probable que sea el propio autor el que acabe leyendo su propio código. Y es muy probable que días, semanas o meses después, el propio autor no tenga fresco el contexto y las reflexiones que le llevaron a realizar la solución de esa manera.
Por ello, debemos realizar el esfuerzo de escribir código para ignorantes. El que venga por detrás no conoce los detalles de la feature que yo sí conozco. Por tanto, necesito disminuir el tiempo de lectura del código que genero para ser mejor programador.
Cómo escribir código fácil de leer
El objetivo es que quien utilice nuestro código no necesite conocer los detalles de implementación del mismo. Y a grandes rasgos, este es uno de los objetivos de la encapsulación en la programación orientada a objetos.
Para mejorar la encapsulación de nuestro código, existen dos principios muy sencillos a los que nos podemos adherir: Command Query Separation Principle y Postel’s Law.
Estos principios nos ayudan a que las clases comuniquen explícitamente cómo funcionan gracias a su diseño.
Command Query Separation Principle (CQS)
Imagina que estás en una aplicación, y cuando utilizas una clase de un tercero te encuentras con este método:
string SendMessage(Message message, User recipient);
A priori parece claro que este método envía un mensaje a un usuario. Pero… ¿qué puede ser el string que me está devolviendo este método? Ya tenemos que empezar a elucubrar y a hacer suposiciones, a priori se me ocurre:
- Un resultado tipo “ok/ko”
- El email del destinatario
- El identificador del mensaje enviado
Realmente, no lo podremos saber con certeza hasta mirar la documentación (si es que existe y no está desfasada), o mirar el código de la implementación de ese método (si es que tenemos acceso al mismo).
Por tanto, el autor del método SendMessage está obligando a sus consumidores a tener que leer código y conocer los detalles de la clase, y por tanto les está haciendo invertir un tiempo que podrían ahorrarse con un mejor diseño.
Y aquí es donde el principio CQS nos puede ayudar. Este principio nos dice que:
Una operación debe ser un command o una query, pero nunca ambas.
Command: son operaciones que modifican el estado del sistema, y no retornan valores (siempre se declaran como void). Al invocarlas, debemos tener en mente que va a cambiar algo y por tanto tener cuidado, es una acción. Ejemplos:
void Save(Order order);
void SendMessage(Message message, User recipient);
void Authenticate(string username, string password);
Queries: son operaciones que no cambian el estado del sistema, y retornan siempre algún valor. Las queries son idempotentes, es decir, da igual que invoques la operación una vez o varias veces, el sistema no se verá afectado. Se pueden invocar con la tranquilidad de que es una simple consulta. Ejemplos:
IEnumerable<Order> GetOrders(int userId);
Message GetMessage(int messageId);
bool ValidateCredentials(string username, string password);
Por tanto, seguir el principio CQS nos ayuda a confiar en el código sin conocer sus detalles de implementación, porque es explícito en lo que hace.
Postel’s Law
También conocido como Principio de Robustez, es un principio muy antiguo pensado inicialmente para protocolos de red, que nos dice lo siguiente:
Debes ser muy conservador respecto a lo que envías, pero muy liberal respecto a lo que recibes.
Tu clase o componente debería avisar al cliente tan pronto como pueda de que el input recibido no es algo que podamos entender; indicando lo que es incorrecto y cómo solventarlo. Por ejemplo, añadiendo guard clauses en caso de que recibamos un parámetro con valor null.
public class DistanceCalculator
{
public int CalculateDistanceInKm(string origin, string destination)
{
if (string.IsNullOrEmpty(origin)
{
throw new ArgumentNullException("You tried to provide a null or empty origin; please supply a valid city name");
}
if (string.IsNullOrEmpty(destination)
{
throw new ArgumentNullException("You tried to provide a null or empty destination; please supply a valid city name");
}
return GoogleMaps.DistanceMatrix.Query(origin, destination);
}
}
Observa que las excepciones deben ser para ayudar a los programadores, y no a los usuarios finales (a quienes se les debe mostrar información no técnica). Así que las excepciones bien explicadas y que ofrecen soluciones también ayudan a hacer tu código más fácil de leer, y pueden servir casi como una especie de documentación.
Por otro lado, cuanto más sencillo sea un objeto devuelto, más fácil de usar será para el cliente.
Hay que preocuparse, por ejemplo, de que si la firma de tu método indica que devuelve un string o un objeto complejo, siempre sea así, y nunca devuelva null. De lo contrario, estamos creando una API inconsistente. Si retornamos null como recurso ante una necesidad, entonces forzamos al desarrollador a leer la documentación o el código, que es precisamente lo que queremos evitar.
A priori, deberías evitar que un método devuelva un valor null.
Para evitar devolver null en ningún caso, podemos apoyarnos en técnicas como TryRead o crear un objeto Maybe.
Todas estas características de ser permisivo con la entrada y riguroso con la salida, se diseñan mucho más fácilmente si utilizas TDD.
Conclusión
Por supuesto hay muchas más variables que ayudan a la legibilidad del código como Clean Code y otras buenas prácticas, pero en este artículo quería centrarme en la encapsulación, porque no hay código más fácil de leer que aquel que no necesita ser leído.
El desarrollo de software no es cosa de frikis solitarios en un garaje, al contrario, es algo muy social y de trabajo en equipo. Por ello, ser sensibles con los futuros lectores de nuestro código y preocuparnos de que sea fácil de entender, nos hace ser mejores profesionales.
Disclaimer: en Kirei Studio no somos fundamentalistas de ninguna tecnología, principio o patrón de diseño. Lo importante de este artículo es el concepto de ayudar a que tu código sea más fácil de leer, y no los patrones o principios, que no dejan de ser herramientas para ello. Por ejemplo, si el contexto lo exige, no tengas miedo a incumplir el CQS si crees que te va a aportar más inconvenientes que ventajas.
Nota: Este texto fue publicado originalmente en el blog de Kirei Studio.