Mostrando entradas con la etiqueta Programación educada. Mostrar todas las entradas
Mostrando entradas con la etiqueta Programación educada. Mostrar todas las entradas

viernes, 27 de agosto de 2010

Pseudo-AOP usando delegados en C#

Estaba yo tan feliz programando un servicio en WCF y me di cuenta de que en todos los métodos de servicio que escribía estaba repitiendo el mismo esquema de código una y otra vez: "Ejecuta todo el método en un bloque try-catch, y si algo va mal, escribe una traza del error y devuelve null (o cero, o el valor por defecto adecuado)". Decidí entonces que debía hacer algo al respecto, primero porque estaba violando el principio DRY (algo inadmisible para alguien que programa con educación), y segundo por vagancia (si se puede ahorrar aunque sea un simple copypaste, hay que hacerlo).

Encontré una solución muy mona que se basa en el uso de un método auxiliar que se encarga del trabajo sucio (capturar la posible excepción y escribir la traza en ese caso) más un delegado que contiene el código que queremos ejecutar realmente. El resultado es algo que, visto de lejos y en una noche sin luna y con niebla, se parece remotamente a la Programación Orientada a Aspectos. Ahora vamos a ello.

Supón que tienes un método (expuesto al exterior por medio de un servicio WCF en este caso, pero no tiene por qué ser así) tal que este:

Bien, quieres añadir control y traza de errores de forma genérica, es decir de forma que no tengas que escribir el mismo código para otros métodos similares. ¿Cómo podemos conseguir esto?

La solución que yo uso pasa por definir un método auxiliar llamado ExecuteMethod, que en su forma más básica es algo así:

Entonces modificamos la firma de nuestro método de servicio de forma que pasa a ser privado, y creamos un método nuevo que invoca a ExecuteMethod pasando el método original como parámetro. Se entiene mejor viendo el código directamente:

Nótese cómo usamos una expresión lambda para pasar de forma limpia y elegante (¡oh, ah!) un delegado que apunta a nuestro método original.

Ahora, podemos aplicar esta técnica de control a todos los métodos que queramos, por ejemplo:


La gracia de esta técnica es que podemos cambiar nuestra estrategia de control de errores y traza cambiando sólo el método ExecuteMethod. Por ejemplo, podemos decidir escribir trazas de error usando el método Debug.Write en un principio, para más adelante usar el registro de eventos de Windows, quedando entonces el método algo así:


También podemos decidir volver a lanzar la excepción en vez de devolver el valor predeterminado del tipo de retorno, o actuar de forma distinta según el tipo de excepción... en fin, las posibilidades son infinitas, o casi.

Para terminar, he aquí la versión de ExecuteMethod para invocar métodos de tipo void (es decir sin valor de retorno):

Esta versión se invoca de la misma forma que la anterior, excepto por supuesto que no es necesaria la palabra return:

En resumen, como dicen por ahí: se non è AOP, è ben trovato.

jueves, 19 de agosto de 2010

Envoltorio para las clases File y Directory

Si en tu código usas algún motor de inyección de dependencias (deberías), sabes que tus clases nunca deben depender de otras clases concretas sino de interfaces, y que los objetos que implementan dichos interfaces deben ser suministrados externamente (típicamente mediante el constructor). Es decir, que en vez de hacer esto:

...tienes que hacer esto otro:

...y usar un inyector de dependencias para registrar la clase apropiada que implemente IMiClaseAuxiliar y para construir los objetos de tipo MiClase. Esto facilita enormemente la creación de pruebas unitarias, especialmente si usas algún mecanismo de mocking.

La cosa pinta de entrada muy simple y muy bonita, pero, ¿qué pasa cuando en vez de objetos propios tenemos que usar las clases de sistema? Por ejemplo si accedemos al sistema de ficheros, ¿cómo simulamos la existencia de determinados ficheros en nuestras pruebas unitarias?

La solución es no usar directamente las clases de sistema implicadas (File y Directory en caso de ficheros), sino crear clases "envoltorio" que tengan los mismos métodos públicos que la clase de sistema original; dichos métodos simplemente delegarán el trabajo en los métodos "reales" de la clase de sistema correspondiente. Creando interfaces para las clases envoltorio, ya podemos usar mecanismos de mocking en nuestras pruebas unitarias.

Como me explico peor que un libro sumergido en cemento, mejor pongo un ejemplo. Si por ejemplo antes teníamos esto:

...ahora definiríamos una clase FileWrapper con su correspondiente interfaz:

...y nuestra clase quedaría:

Pues bien, resulta que alguien más ya había pensado esto y ha creado sendos envoltorios para las clases File y Directory. Os pego el código aquí para que sea de más fácil acceso. De nada.

lunes, 28 de agosto de 2006

La ley de Demetrio

He aquí un claro ejemplo de lo gracioso que puede llegar a ser traducir frases en inglés. En este caso, todo un señor paradigma de la programación orientada a objetos (The Law of Demeter) nos queda reducido a algo que parece el título de una película setentera de Pajares y Esteso.

La susodicha ley especifica un método para construir clases lo más desacopladas e independientes posible, de forma que la reutilización de componentes y el mantenimiento de aplicaciones resulten tareas algo más sencillas. Concretamente, la ley dice que un método de una clase sólo debería invocar métodos pertenecientes a:
  1. El propio objeto.
  2. Los objetos creados por el método.
  3. Los objetos pasados al método como parámetros.
  4. Los campos y propiedades del propio objeto.
Si seguimos esta ley a rajatabla, esto implica, por ejemplo, que no podemos invocar métodos de objetos que nos son devueltos al invocar otros métodos. Esto puede llegar a ser complejo de cumplir.

Otra consecuencia más "razonable" es la ausencia de variables globales. La información de contexto apropiada debería estar contenida en el propio objeto como una propiedad. Aunque esto también parece complejo de cumplir, ahora veremos un ejemplo de cómo hacerlo de forma sencilla.

Tomemos por ejemplo el recurso global por excelencia de cualquier aplicación (por lo menos del tipo de aplicación que yo desarrollo): la conexión a la base de datos. Supongamos que necesitamos un objeto SqlConnection omnipresente, accesible desde cualquier lugar de la aplicación. Lo típico en estos casos es crear una clase estática de estado y almacenar la conexión como una propiedad de la misma. Entonces, las clases que necesitan acceso a la base de datos no tienen más que leer la susodicha propiedad de la susodicha clase.

Pues bien, según Demetrio, esto está MAL.

En primer lugar, la conexión no debería ser accesible a cualquier clase de la aplicación, sino sólo a aquellas de la capa de acceso a datos. Pero eso es otro tema, y tampoco quiero ponerme excesivamente repelente.

En segundo lugar, toda clase que necesite acceso la base de datos debe tener una propiedad que albergue la conexión, de esta guisa:

De esta forma, si en el futuro queremos reutilizar la clase en otro proyecto, podremos hacerlo sin problemas, puesto que no habrá ninguna dependencia de datos globales.

Y tu dirás, "¡Pero yo creo instancias de esa clase en mil sitios en mi código! ¿Tengo que establecer la conexión cada vez? ¡Qué pesadilla!" Y razón no te falta. Pero existe una solución muy sencilla: crear una propiedad estática que contendrá el valor predeterminado para la propiedad, es decir, el valor que tendrán las instancias nuevas si no se especifica lo contrario. Verbigracia:

Ahora sólo tienes que establecer el valor de la propiedad estática una vez, en el método Main que da inicio a la aplicación:

...y si para una instancia en particular quieres establecer otra conexión, puedes hacerlo tranquilamente mediante su propiedad Connection.

Y como hoy estás un poco gruñón, volverás a quejarte: "Vale, pero cada vez que creo una clase nueva con acceso a datos, tengo que acordarme de inicializarle la conexión predeterminada en el Main. Y yo tengo muchas clases y bla bla..." Tranquiiilo, que esto también tiene una solución facilona. Es hora de que la reflexión venga al rescate.

No, no se trata de que nos volvamos filósofos de repente. La reflexión es el mecanismo de .NET que nos permite obtener información sobre los componentes de cualquier ensamblado: podemos listar las clases existentes, los miembros de una clase, invocar métodos especificando su nombre, etc.

Usando reflexión, podemos decirle a nuestra aplicación lo siguiente: "Búscame todas las clases del ensamblado actual que tengan una propiedad llamada DefaultConnection, y les estableces la propiedad con este valor". Dicho y hecho:

Una solución más limpia sería crear un interfaz (por ejemplo IConnectable) que tuviera como propiedad la conexión y su alter-ego estático, y hacer que las clases con derecho a acceso a la base de datos implementaran ese interfaz. Pero eso te lo dejo como ejercicio (que además ya estoy cansado de escribir).

Conclusión: haz caso a Demetrio, que es más viejo que tú y sabe mucho.

Más información aquí.