Design patterns in the test of time: VISITOR

@Ayende ha comenzado una serie de post en lo que revisa algunos patrones de diseño (Go4) luego de transcurridos más de 18 años desde su formalización en el libro Design Patterns: Elements of Reusable Object-Oriented Software.  Inspirado por esa serie, y por el hecho de que en mi última entrevista laboral me preguntaron sobre esto,  voy a presentar una revisión sobre el patrón visitor.

 

Visitor en su forma clásica

La motivación detrás de este patrón es poder añadir funcionalidad a una estructura de datos sin necesidad de modificar las clases que conforman esa estructura. En otras palabras: separar las estructuras de datos de aquellos algoritmos que operan sobre ellas. Si vamos a la Wikipedia vamos a ver este diagrama:

Technorati Tags: ,,

Como se ve, tiene referencias cíclicas, es decir, todos los CarElements deben implementar un método accept que recibe una referencia a un CarElementVisitor y todo CarElementVisitor matiene referencias a todos los tipos que implementan la interface CarElement. Esto es porque C# (el diagrama usa java) no posibilida doble dispatch y así, mediante estas referencias cíclicas es cómo se logra simularla.

Aparte del fuertísimo acoplamiento al que nos fuerza, tiene el inconveniente de ser un patrón que agrega bastante complejidad y que es poco legible debido a su “extraña” sintaxis. Así, si tuviésemos un AST y quisiésemos volverlo código mediante un visitor, nuestro código se vería así:

image

Pero si tratamos de seguir este código, y vamos directamente a la implementación de este método ‘Accept’ nos encontraremos con esto:

image

Y por último, si nos salteamos la abstracción IVisitor y vamos directamente a la implementación del método Visit del PrinterVisitor, nos encontramos con esto otro:

image

Si no conoces el patrón, posiblemente no comprendas este código (y tengamos en cuenta que nos hemos salteado las abstracciones!). Pero el motivo más frecuente por el que no se entiende, si es ese el caso, es porque es complejo.

 

Visitor alternativo

La propuesta que dejo no es mia, ni es nueva. Esta es una implementación muy popular en lenguajes dinámicos.

Partamos del mismo supuesto: tenemos un AST al que queremos transformar en un string. Lo primero que podemos cambiar es la sintaxis confuza del visitor clásico mediante el uso de un Extension Method, como sigue:

image

Ahora veamos cómo se ven los nodos de este árbol:

image

Si bien aquí tenemos una interface INode la cual no luce muy bien, la simplicidad de los nodos es evidente ya que no estamos forzando a implementar ningún método extraño, me refiero al método ‘Accept’. Peo vamos a ver la implementación:

image

Esta implementación es mucho menos compleja y legible, además de reducir notablemente el acoplamiento (los nodos no necesitan conocer al IVisitor ni implementar ningún método extra) lo que posibilita eliminar las referencias cíclicas.

Lucas Ontivero

Creando la Matrix

Tengo que bloguear esto porque es muy gracioso! Es una conversación con el cliente que estoy teniendo en este momento.

image

Será que el agente Smith realmente nos está infectando el sistema?

Nivelando para abajo

Leía este post de Luis Ruiz Pavon acerca del comportamiento de los closures en el que Luis anima a responder cual es la salida de un bloque de código en el que se devuelve un array de expresiones lambdas que referencian a la variable utilizada para iterar dicho array (closures) y entonces veo este comentario de Eduard Tomàs i Avellana:

image

El link al que referencia Eduard pertenece al blog de Eric Lippert: Closing over the loop variable considered harmful, una vieja entrada que había leído en su oportunidad pero que desestimé porque nunca pensé que llegara a tomarse la decisión de introducir un breaking change en el lenguaje para solucionar algo que NO ESTÁ ROTO, repito, NO ESTÁ ROTO.

Está claro que introducir este tipo de cambios no le gusta a nadie, y que por lo tanto debe de haberse meditado, y mucho. Pero ¿qué es lo que anima semejante cosa? Es decir, si vamos a romperle el código a muchos programadores es porque pensamos que es lo mejor para todos, ¿o no?

Veamos, ¿quiénes pueden verse afectados? de seguro todos aquellos que entiendan lo que son los closures y comprendan el scope de las variables y, ¿quienes pueden verse beneficiados? pues todos aquellos que no entienden esto. Y es por eso que estoy en desacuerdo, porque lo que se intenta una vez más es hacer que el lenguaje sea el guardián de la correctitud del software y no el desarrollado.

En mi experiencia, es absolutamente imposible defenderse de un desarrollador que no sabe lo que hace.

ECMA Harmony y la conquista del mundo

Dicen que existe un plan para que javascript domine el mundo en los próximos años, y a decir verdad, y viendo las propuestas que se plasman en el draf del ECMA-262 sexta edición, creo que el plan aunque algo silencioso sigue a marcha firme.

Quiero mostrar aquellas propuestas que a mi me resultan más interesantes:

Strings

Lo bueno: se agregan métodos startsWith(), endsWith(), contains(), repeat(), toArray()

Lo malo: la interpolación de strings no forma parte del draft actual, esta feature permitiría crear string de manera más natural, como por ejemplo:

image

en lugar de usar las formas actuales:

image

Destructuring

Esta es una de mis preferidas y permitiría entre otras cosas que una función retorne más de un resultado:

image

Iterar las propiedades de los objetos de manera más natural:

image

y quien sabe cuantas cosas más!

Iterators

for-of para iterar sobre los valores de las propiedades en lugar de hacerlo sobre las propiedades es sí mismas.

image

Generators

Esto nos tienen que resultar familiar a todos, solo hace falta ver el yield para darse cuenta de cómo funciona. Exactamente como se hace en .Net. Ahora podríamos por agregarle al prototype una función where, por ejemplo, para evaluar un predicado contra cada uno de los elementos del iterador y listo, tendremos linq en ECMAScript. (pero mejor ver Array comprehension)

image

Lo mismo pero quizás más claro y conveniete puede realizarse con un Generator comprehension:

image

Lambdas

Aunque actualmente no forma parte del draft de la versión 6, la discusión está vida. No hay que explicar nada.

image

Array comprehension

image

¿Quien no entiende esto? es un generator que retorna los cuadrados de los números pares de numbers.

Let

Si bien la idea detrás de la sintaxis de javascript era parecerse a java, pocos lenguajes se parecen tan poco. El scope de las variables en javascript es la función y no el code block como en java. Let es justamente para limitar el scope al code block.

image 

Estas son solo unas pocas de las propuestas que se discuten actualmente y que me han gustado más. Otras más importantes quizás sean las de modularidad del lenguaje, pragmas y API, pero a mi me gustan las que tratan sobre la expresividad del lenguaje y la verdad es que con estas y otras propuestas tomadas de ruby y python, ECMAScript va a dar un lindo paso en su plan de conquista del mundo.

Cómo crear un Compact Framework (I)

Imaginemos los siguientes escenarios:

  • tenemos un gran framework super completo del cual queremos obtener una versión más pequeña para correr en dispositivos móviles, y tenemos que quitar ciertas características que el dispositivo no soporta. Además la idea del concepto de “Compact” Framework también hace referencia al tamaño, por lo que queremos eliminar todo el código del las características no soportadas.
  • queremos lanzar varias versiones de nuestra aplicación como Free Edition, Profesional Edition, Enterprise Edition, y no deseamos que en cada una de las ediciones solo vaya el código necesario para esa edición (y no todo).

¿Cómo lo hacemos?

La idea que propongo, si bien no es original ya que tengo entendido que .Net Compact Framework se ha realizado con una técnica similar, es simplemente identificar en los assemblies todos aquellos fragmentos de código (IL) que se utilizan para implementar las características deseadas y una vez conocido esto, eliminar el resto. Es decir, eliminar todos los métodos, propiedades, campos, eventos, tipos y demás elementos que no tomen parte en la realización de las características que queremos.

Por ejemplo:

En un procesador de texto tenemos 2 features: Save y Print. Y la interacción como se ve abajo.

image 

Del diagrama queda claro que no todas las funcionalidades hacen uso de todos los tipos ni de todos los métodos ni de todas las propiedades, etc. Entonces, si solo quisiésemos permitir guardar un documento, sería seguro eliminar los tipos DocumentReader y Printer, verdad?

Esto es posible hacerlo reescribiendo los ensamblados con Mono.Cecil, solo hay que conocer algo de IL para hacer cosas sencillas aunque para hacerlo realmente bien hay que conocer IL a la perfección.

Veamos el código del ejemplo del cual queremos eliminar la feature que permite imprimir es documento.

image

Ahora bien, antes de continuar quiero mostrar el resultado de como queda el assembly antes y después de ser procesado:

image

Lo que se eliminó es la posibilidad de imprimir y por eso, en el assembly procesado no existe el método TargetClass.Print(), ni los field _reader y printer ya que estos solo eran usados por el método TargetClass.Print(). Los tipos Printer y DocumentReader también han sido eliminados ya que no eran usados por otro método más que el TargetClass.Print().

Por otra parte, todos los tipos, campos, propiedades y eventos que son utilizados por otras características, aún cuando sean utilizados además por el TargetClass.Print(), permanecen intactos. Para esto solo hace falta comprobar que la clase Document aún sigue allí (aunque modificada ya que la propiedad Title se ha quitado porque solo era relevante al imprimir).

Bien, la idea entonces es que una vez que conocemos cuales son los métodos que implementan las features que deseamos mantener, la técnica consiste en seguir todas y cada una de las llamadas que ese método realiza. Luego, hacer lo mismo con cada uno de estos de manera recursiva hasta tener la lista de todos los métodos que participan en la realización de la características deseada. Abajo puede verse el mecanismo:

image

Y con esto ya tenemos el listado de todos los métodos usados, ahora lo que hacemos es eliminar todos aquellos métodos que no se encuentren en la lista:

image

Esto elimina todos los métodos no usados incluyendo getters, setters, los add y remove de los eventos. Por esta razón es que de seguro nos quedan propiedades sin gettes ni setters y que por lo tanto tenemos que eliminar para que el ensamblado nos quede funcional.

image

Por último, si existen clases sin métodos, ni campos, ni propiedades ni eventos… la eliminamos también.

image

Como esto es solo una prueba de concepto, quizás cambie muchas cosas en el código, voy a continuar con la explicación en la siguiente entrada.

Patrón repetido en MVC. Mi propuesta

Cuando usamos ASP.MVC uno de los patrones que debemos respetar es el de “Un modelo entra, un modelo sale” y otro muy común es el de validar el modelo y si este no es válido devolverle la misma vista al usuario para que corrija los datos de entrada. Por esto es común ver el siguiente patrón:

image

¿Qué está mal aquí? ¿Lo ves?

Lo que está mal es que este patrón se repite en cada una de las acciones de cada uno de los controladores de cada una de las aplicaciones que hacemos con MVC Framework y eso huele muy pero muy mal (DRY).

Este es el tipo de casos en los que puede ayudarnos AOP, y MVC framework hace un trabajo espectacular al posibilitarnos implementar AOP mediante filtros. Entonces mi idea es que esto debería hacerse así:

image 

Ahora sí el método hace lo que tiene que hacer, ni más ni menos. Para esto (y solo para demostrar mi punto) he creado el atributo ModelValidationAttribute (necesita un mejor nombre!) como sigue:

image

Y esto funciona a la perfección! Ojo, no recomiendo usarlo sin antes hacer una buena implementación ya que le faltan cosas al ViewResult como TempData entre otras (y el OnActionExecuted vacio tiene un olorcito medio feo – pero no es culpa mia! ¿por qué el creador de la interface IActionFilter me obliga a incumplir el contrato? ¿No serán 2 interfaces distintas [LSP]?)

Bueno, esto como les dije funciona a la perfección. La prueba:

image

Saludos.

Viendo la web como en 2006

Muchas veces uno tuvo un sitio que dejó en el olvido y quisiera verlo nuevamente o quizás recordar cómo se veía Geeks.ms en el 2006. Bueno, para los segundos les muestro cómo se veía esto en 206. Que lindos tiempos! las fechas de los artículos en inglés, nada de publicidad, un artículo de Carlos Fouz sobre la batalla que Microsoft la daría a Google y las 18 visitas que tuvo, algo que hoy tenemos en 15 minutos. 

image

 

Bueno, la verdad es que esta entrada es solo para mostrar algo que probablemente ya muchos conocen pero que para aquellos que no, les resultará interesante. Se trata de http://web.archive.org donde podemos ver el contenido de la web en las fechas que queramos.

10 consejos para que no te crackeen tan fácil

Crackear una aplicación tiene algo de arte y de ingenio pero crackear una aplicación .Net es demasiado sencillo para mi gusto, y representa un problema para la empresa o desarrollador que la comercializa y vive de mantener y mejorar el producto. Ya no vale eso de “total quien se va a dar cuenta”, eso no es válido para ninguna aplicación pero esto es aún peor cuando se trata de herramientas de desarrollo de software las cuales serán utilizadas por profesionales que conocen como pueden saltearse los sistemas de licencias.

Hoy, con todas las herramientas de que disponemos en .Net (ILSpy y sus addins de debugging, Cecil, RegSpy, entre otros) saltearse estas validaciones es demasiado sencillo. Por eso quiero hacer algunas pocas recomendaciones que si bien no pueden por si solas detener a quien se lo proponga, por lo menos le dificultará un poco la tarea. Esto es importante desde mi punto de vista ya que si debo invertir una semana para crackear una aplicación quizás me convenga comprarla.

Aquí van:

1. Firma los ensamblados. Esto hará que no puedan reemplazarlo por otro o reescribirlo; caso contrario lo único que debe hacer alguien para saltearse las validaciones es reescribir algún método y listo. Para ilustrar esto veamos un ejemplo, imaginemos que tenemos una clase encargada de verificar la licencia del usuario llamada LicenseChecker la cual tiene un método CheckLicense que retorna TRUE si la licencia es válida y FALSE en caso contrario. En este caso, solo bastaría con reescribir el método insertándole un return true. (ldc.i4.1 ret) Abajo puede verse lo ridículamente sencillo que es lograr esto con Cecil. 

image

Del mismo modo, no guardes información valiosa en constantes publicas ya que de la misma manera, se puede modificar sus valores. Por ejemplo, imagina que en una clase LicenseConstants tenemos un field llamado TrialDays con un valor de 30 que se asigna en el constructor estático en la instrucción nro. 10. en este caso, lo más sencillo será sobrescribir el assembly para asignarle un valor mayor y así extender el período de prueba tanto como queramos. El código sería así:

image

 

2. Nombres de identificadores. Nada va a salvarte de ti mismo, si la clase que verifica la licencia se llama LicenseChecker, LicenseValidator o de alguna otra forma altamente sugestiva, cualquiera con medio dedo de frente comenzará por ahí. Simplemente buscará con Reflector o ILSpy la pablabra “License” y listo!
Recuerda que los ofuscadores no pueden cambiar los nombres de los identificadores que forman parte de la superficie pública porque de lo contrario el assembly quedaría inutilizable. Sabiendo esto, te darás cuenta que todo lo que suene altamente sugestivo no puede formar parte de la superficie pública.

3. Ofusca los ensamblados como parte del proceso de Build. Si bien esto tiene pros y contras, con esto no solo dificultas un poco la tarea de examinar el código mediante Reflector, ILSpy u otros sino que además proteges un poco el código de copias ya que la mayoría de estas herramientas permiten extraer el código fuente en proyectos compilables.

4. Strings. Los strings con mensajes que se muestran en la UI referidos a temas de licencias deben estar encriptados de alguna manera. Por ejemplo, si alguien quisiera romper con el componente de validación de licencias y ve que en la UI dice “Enter License Number:”, lo primero que hará será buscar ese string en los assemblies y una vez que lo encuentre se fijará en donde se usa, y desde allí llegará a donde quiera.

5. Encriptación. Encriptar datos no detendrá a nadie aunque sí hará más lento el proceso de cracking y quizás llegue a frustrar a la persona que intenta saltearse la licencia. Lo que hay que tener en cuenta es que no debe ser la clave pública fácil de encontrar. Otro punto aún más importante es que los métodos que encriptan y desencriptan no deben nunca estar en assemblies separados de donde se usan porque de lo contrario estos tendrán que ser públicos y con esto ya arruinamos todo. Es decir, nunca poner estos métodos en un assembly “Utils” con nombres tales como Encript y Decrypt y hacerlos públicos, de lo contrario, cualquiera creará un proyecto de consola, referenciará a ese assembly y buscará las en el registro aquellas claves creadas por la aplicación que se ven claramente como encriptadas. Es decir, aquellas que se ven como sigue “9L3a1IQLzZFOmlU/3GkYu5Dm1ijRA+”, todo el mundo sabe que ésta es una cadena que ha sido encriptada.

6. Registro de windows. Ya sea que el código esté o no ofuscado, los strings con las claves del registro no deben reconocerse fácilmente. Si en alguna parte tenemos un string que comienza con “HKCUSoftware”, ya está! Si además ponemos públicos los algoritmos de desencriptación ni siquiera será necesario buscar la clave de desencriptación, solo será necesario hacer algo así:

image

Luego modificamos el valor que obtuvimos por consola y lo encriptamos nuevamente (gracias a los métodos que nos brinda el mismo producto) y lo escribimos en el registro.

7. Períodos de prueba. La mayoría de los productos permiten ser usados por un período de prueba, este es uno de los puntos más débiles de casi todos los productos que conozco ya que para lograrlo, por lo general deben guardar la información de activación, días de prueba o fecha de finalización en alguna parte de nuestros equipos (por lo general, en el registro de windows). Suele ser muy sencillo manipular las claves del registro para que ese período de evaluación se extienda ad infinitum. Recuerda que quien intenta saltearse la licencia, tiene el código a su disposición por lo que encontrará la manera de burlar el mecanismo.

Ten en cuenta que puede espiarse la actividad que el sistema realiza en el registro al momento de aceptar la licencia de evaluación con lo que encontrar qué es lo que hay que modificar no es el problema sino el cómo hay que modificarla. Ahí es donde debe ponerse toda la inteligencia. 

8. Datetime.Now. Restringe al máximo el uso de Datetime.Now para checkear las diferencias de días que le quedan antes de que expire el período de evaluación, ya que este devuelve la fecha del sistema y esta es modificable por el usuario. Es decir, le damos al usuario una variable que él puede modificar a su antojo.  Por lo tanto verifica todos los casos posibles, piensa que el usuario puede cambiar la fecha del sistema al momento de la instalación, piensa en lo valores límites, piensa si la diferencia (DateTime.Now – InstallationDate) es positiva y si es negativa, piensa que lo más fácil para el que menos sabe será jugar con la fecha del sistema.

9. Prueba. Muchas empresas desarrollan sus sistemas de licencias y realizan pruebas funcionales sobre estas para comprobar su correcto funcionamiento. Esto es necesario pero es solo una prueba de caja negra; sobre estos sistemas deben realizarse pruebas de caja blanca y dedicar al menos un día de, al menos, uno de los mejores desarrolladores a probar si puede vulnerar el sistema.

10. Dedicarle el esfuerzo y la inteligencia necesaria. Si no querés que las instrucciones de cómo vulnerar tu sistema se encuentren al otro día de realizado el release  accesibles en toda la internet, si no querés que los seriales estén por todos lados, si no querés perder ventas, entonces dedicale el tiempo, esfuerzo, inteligencia y recursos necesarios. Quizás te convenga contratar a un experto en el tema.