El último año he dedicado la mitad de mi vida laboral a desarrollar interfaces para el manejo de registros de personas, i.e.: UI, plugins/widgets, web services, stored procedures, etc. Dentro de la información que se persiste, como parte de requerimiento de negocio, el cálculo automático de los diferentes tipos de identificación es indispensable.
Dado que los proyectos en los que estoy involucrado son consumidos en LATAM, he tratado con identificaciones de diferentes países, sin exagerar por mencionar:
- México: RFC, CURP
- Colombia y Chile: RUT
- Panamá, Perú, Puerto Rico y Ecuador: DNI, RUC
- Argentina: CUIT, CUIL, DNI, CDI
Y aunque esto es bastante normal, muchas aplicaciones que involucran información de personas requieren estos pequeños cálculos, puede llegar a convertirse en tu talón de aquiles.
Para México, es decir, para el cálculo del RFC, ya se contaba con una pequeña librería producto de un desarrollo in-house, que ha pasado de generación en generación, tras rotación y rotación, de un incontable número de desarrolladores. Existe código suelto, parches, código no comentado, en fin… es una librería a la cual no se le ha dado cariño prestado mucha atención.
No me atrevo a juzgar -porque sé que cometo un montón de errores- si ha sido o no un buen desarrollo, si cuenta con best practices, si tiene un performance aceptable, si es una librería testable, etc. Sin embargo, no puedo negar e ignorar, que me ha sido bastante complicado modificar esta librería; fué necesario debuggear línea a línea y un par de horas en estatus Don't disturb
para entender qué/cómo/cuándo/dónde funciona. Y aún así, al día de hoy se siguen presentando problemas en ciertos escenarios muy particulares.
Además, -hasta donde sé- no hay evidencia de la base de la construcción de esta librería; es decir, las reglas para el cálculo no están alineadas a un documento oficial. Si a esto le sumamos el hecho de que la librería es de código cerrado, me ha parecido justo y necesario buscar alguna alternativa.
Búsqueda
Lo habitual, primero hacer un “barrido” rápido por la web google para identificar si alguien más se encuentra trabajando con el cálculo del RFC; podría sacar provecho y quizá alguna referencia.
Casi obvio, sí. Montones de implementaciones para el cálculo en diferentes lenguajes, los hay en: C, C++, VB, VB Script, Fox Pro, Progress, Java, Javascript, T-SQL, PL/SQL, Excel, Excel con macros, etc. Los más astutos te venden un programa con su respectivo wizard que realiza el cálculo.
La mayoría de las implementaciones que he encontrado, se quedan bastante cortas y con una lógica en su código algo extraña diferente.
En fin, concluí la búsqueda al encontrar una implementación en Java escrita por @josketres. Me ha parecido interesante y, por los argumentos basados en documentos oficiales, decidí basar mi código en esta implementación.
Resultado
El resultado, RFC Fácil .NET. El código fuente se encuentra en este repo.
No quiero entrar en detalles de la lógica y el contexto, pero me ha parecido interesante compartir el enfoque de la fachada de esta librería.
Fluent Interface
Fluent Interface es un estilo de implementación de código, si me permiten definirlo asi, para hacer más legible -o al menos en teoría- la programación. Este concepto viene atribuido por Martin Fowler por ahí del año 2005.
Lo interesante es el estilo al escribir código, la idea es que sea de manera fluida la invocación de métodos y sean apreciados como un conjunto de pasos o requerimientos para construir un objeto. Para los que conviven en el mundo de javascript, será bastante normal el término de Method Chaining y es este mismo enfoque el que se la dá a la POO con la técnica de Fluent Interface.
Contexto
Volviendo al tema, sabemos que para cacular el RFC es necesario tener de entrada la siguiente información de una persona:
- Nombre(s)
- Apellido(s)
- Fecha de nacimiento
Lo habitual en POO sería, tener un constructor para inicializar un objeto Rfc
, establecer propiedades a través de este y entonces realizar el cálculo. Visto desde el punto de vista cliente, se tendría algo así:
¿Cierto?
Ahora bien, supongamos un descuido al instanciar el objeto. Podría llevar a hacer algo como lo siguiente:
Es decir, dado que los primeros tres argumentos son de tipo string
, no hay una forma de advertir al programador si debe ir primero el nombre y en segundo lugar el primer apellido o incluso alguna otra variación. Claro, excepto si se conocen o trabajan juntos viven juntos.
Es cierto, desde C# 3.0 podemos inicializar un objeto con las propiedades expuestas:
Sin embargo, para un constructor con un número relativamente grande de parámetros de entrada, sería bastante ilegible. Incluso, pensemos en una composiciónanidada; una inicialización de las propiedades complejas desde el constructor sería bastante tediosa e ilegible.
Implementación
El punto de inicio para RFC Fácil .NET, partirá de la clase RfcBuilder
. Esta clase, contendrá los métodos chaining para la asignación de las propiedades.
Nótese que en cada método, después de establecer el valor de su respectiva propiedad, se retorna la misma instancia en cuestión con el uso de this
. Este retorno, hará que el método se convierta en chainable.
Dado que cada método retorna una instancia de la clase RfcBuilder
, requerimos un método de acceso para la instancia que inicialmente pensamos construir, es decir, la instancia de la clase Rfc
.
Del código anterior, el método Build
se encargará de orquestar las llamadas para el cálculo de cada parte del RFC. Lo importante a destacar aquí, es que este método retornará el tipo Rfc
a través de la invocación de un método estático de la misma clase.
El contenido de la clase Rfc
, quedaría algo como lo siguiente:
La intención de tener un constructor privado y acceder a este mediante un método estático, radica básicamente en evitar que el cliente construya una instancia de la clase Rfc
con el operador new
. De esta manera, se forzará a invocar el método Build
, de la clase RfcBuilder
, para mantener unúnico punto de acceso a la instancia.
Teniendo esto, nuestro código cliente que implementa el cálculo del RFC, se vería de la siguiente manera:
Ahh!, mucho mejor =D
Conclusión
No estoy del todo seguro si este estilo sea el más adecuado. Hay bastantes opiniones en contra de usar este enfoque; en general tiene una mala reputación.
De cualquier forma, creo que para el contexto en cuestión ha quedado bastante bien este estilo. Me parece que le dá mucha legibilidad, es práctico y minimiza el descuido al momento de invocar cada método. ¿No?