domingo, 24 de junio de 2007

El principio de las cosas... Parte 1 de 2

Hace unos días un amigo me planteaba su dificultad para comprender IoC, Inversion of Control, mientras conversábamos sobre el tema me di cuenta que el problema no radicaba en el principio en sí, si no en la necesidad de aplicarlo, el porque de su aparición es escena.

Me parecio un buen aporte entonces escribir esta entrada, la idea es mencionar algunos principios de diseño OO, Object Oriented, sobre todos aquellos implícitos en conceptos mas avanzados y que muchas veces damos por sentado que nuestro interlocutor conoce.

La otra realidad que viene de la mano es que difícilmente quien se inicia, y algunos ya iniciados, logren diseños eficientes sin conocer estos principios o su significado concreto.

Principios de diseño:

a - Responsabilidad única.
b - Principio Abierto / cerrado
c - Principio de Sustitución
d - Principio Inversión de dependencia
e - Segregación de interfaz

Veremos ahora que cuestión en el diseño ocupa a cada principio, personalmente me parece más didáctico explicar algunas cosas por el opuesto, mostrar el problema y asi entender la ventaja de aplicar el principio.

a - Responsabilidad única.

Vamos a tomar un pequeño conjunto de clases para nuestro ejemplo, empezaremos con una y veremos los males que nos aquejan.



Nuestra clase Cliente es verdaderamente potente, sabe hacer tantas cosas!!!!, con solo tener una instancia de ella puedo resolver una gran cantidad de problemas.

Veamos que cosas sabe hacer:

1 - Persistirse
2 - Eliminarse
3 - Crear una salida HTML que muestre sus atributos
4 - Hacer reportes referidos a ella misma
5 - Calcular su edad en el mercado

Todo parece muy cómodo, pero analicemos como se comporta esta clase frente al cambio, su capacidad de adaptación.

  • Cambio mi motor de base de datos, de XXSql paso a NNSql.
    Debo cambiar mi clase Cliente.

  • Cambio la forma de crear la salida HTML
    Debo cambiar mi clase Cliente.

  • Agrego un reporte o modifico uno existente
    Debo cambiar mi clase Cliente.

  • Cambio la política para calcular la presencia en el mercado
    Debo cambiar mi clase Cliente.
Esto evidentemente es malo, existen distintos eventos cuya consecuencia es un cambio en la clase Cliente.

Si vemos un poco mas allá de nuestras narices notaremos que además esto implicará cambios en aquellos artefactos que recurren a la clase Cliente para resolver alguna colaboración.

Bueno el problema es que nuestra clase Cliente tiene muchas responsabilidades, incluso algunas que exceden claramente sus responsabilidades naturales, me refiero a las responsabilidades de Cliente en su contexto de negocio.
Se dice entonces que Cliente es poco cohesiva, sabe hacer más de lo que necesita, maneja temas en los cuales no es experto.

Posiblemente esa falta de cohesión nos lleve a otro problema, para mi es una regla que se cumple con rigor matemático, acoplamiento.

Un cambio en Cliente requerirá cambios en quienes consumen la clase Cliente, los cambios se propagan en nuestro diseño, el impacto de un cambio es alto, afecta a varios artefactos.

Pero, volvamos a nuestro principio de OOD, Object Oriented Design, Responsabilidad única.

Este principio ataca el problema descripto, su enunciado dice:

No debe existir más de una razón para cambiar una clase.

En nuestro escenario Cliente debería ser algo parecido a la siguiente figura:
Que sucede entonces con aquellos metodos que ya no existen en Cliente?, bueno será nuestro trabajo buscar quienes tienen la responsabilidad de resolver esas cuestiones. Lo importante aquí es que Cliente atienda sus responsabilidades directas, aquellas en las que es experto.

También es destacable que deberá relacionarse, u otras clases deberán relacionarse con ella, para resolver las cuestiones en las que Cliente no es el experto, pero requieren de su colaboración.

Eso nos lleva a otros principios que ayudarán a manejar las colaboraciones en forma desacoplada.

b - Abierto / cerrado

Volvamos ahora a nuestra nueva clase Cliente, en el punto anterior mejoramos su cohesión eliminando algunos métodos, todos sabemos que alguien deberá encargarse entonces de asumir la responsabilidad de implementar esa funcionalidad.

Una posible solución para lo relacionado con la persistencia podría ser la siguiente:

Muy bien otra vez contamos con unas clases muy bonitas, las responsabilidades ahora parecen estar asignadas correctamente, y de hecho lo están.


Propongamos un cambio en el requerimiento y veamos.

Nuestra clase ClienteDB recurre a un susbsistemas de APIs específicas de XXSql. Ahora nos piden que debemos armar una implementación para que la base de datos sea ZZSql.


Nuevamente un cambio tiene impacto directo un cambio en el sistema de base de datos puede requerir un cambio en Ventas. Por caracter transitivo ventas ha quedado acoplado la API de XXSql.

Problema planteado, que nos dice el principio Abierto/Cerrado?
Los artefactos software deben ser abiertos para su extensión, pero cerrados ante una modificación.

En resumidas cuentas lo que quiere decir esto es que un cambio en el entorno de un artefacto no debería implicar un cambio en el artefacto. Esto no solo es válido para clases, se aplica también a métodos y cualquier otro artefacto software.

La aplicación del principio a nuestro problema podría ser agregar una interface o una clase abstracta que sea bien conocida por Ventas, ClienteDB debería implementar la interface o heredar de la clase abstracta.


En el diagrama se puede apreciar la implementación para una cuestión más terrenal, mockobjects y test unitarios.










c - Sustitución

Vamos a aprovechar la capacidad de extensión que nos brindan los lenguajes OO, estamos frente a nuestra clase Cliente, con algunos cambios ya que los requerimientos del sistema han cambiado con el transcurso del tiempo y ahora un nuevo requerimiento nos exige considerar Clientes presenciales y Clientes virtuales.

Hacemos OOP, Object Oriented Poragramming, contamos con los beneficios de la herencia, heredemos de Cliente.

La funcionalidad nueva esta relacionada con la obtención de la autorización de una transacción comercial.

El experto en autorización requiere que el cliente se identifique, pero el ClienteVirtual implementa el metodo Identificarse() disparando una Exception. Su lógica de negocio no provee un mecanismo de identificación.

El método Obtener entonces deberá tener un tratamiento especial para ClienteVirtual.

Posiblemente el programador haga algo así:

if( typeof... )
//Tratamiento para ClienteVirtual
else
//Tratamiento para Cliente

El problema entonces es que CentroAutorización ya no puede tratar a ClienteVirtual como Cliente, debe conocer las características de la sub clase porque esta altera el comportamiento de la clase Cliente. ( Comportamiento, no implementación de comportamiento).

La solución en este caso es revisar eol diseño, posiblemente ClienteVirtual no este en la línea de herencia de Cliente, tal como pensamos en principio.

No atender estas cuestiones hace que nuestro diseño sea críptico, que los programadores no puedan confiar en la extensión de las clases y deban conocer el comportamiento de de cada una de ellas.

Bueno, para evitar esto debemos respetar el principio de sustitución. El mismo expresa:

Toda clase debe poder ser reemplazada por cualquiera de sus subclases

La aplicación de este principio garantiza de alguna manera el éxito en la aplicación de los principios de Responsabilidad única y Abierto / cerrado.

En los próximos días completaré esta entrega con los principios restantes.

Espero que pese a lo básico este post les resulte de interés.




jueves, 21 de junio de 2007

Performance Bodyguard

Este no va a ser el post mas ingenioso de todos, pero realmente es muy util. Ayende publico en su blog ayer un HttpModule para ASP.NET que controla los queries que hace NHibernate por request, de esta forma se pueden ver los excesos de queries en la ejecucion de la pagina.
El post es este:

http://ayende.com/Blog/archive/2007/06/20/Shocking-Rob.aspx

Para hacerlo funcionar es necesario:
- referencia a la DLL de log4net
- eliminar todos los using de Rhino.Commons
- Agregar un item en los Settings del proyecto con el nombre MaxNumberOfQueriesPerRequest, de tipo Int y valor 30 (por ejemplo...)
- Configurar log4net (Es importante configurar el Logger level de NHibernate.SQL a DEBUG)

Arquitectura multi-plataforma: Silverlight@Linux = Moonlight

Un interesantísimo detalle sobre el proceso de implementación de Silverlight en Linux, por Miguel de Icaza.

Es notable el hecho de que tienen casi completa la implementación en 21 días. Creo que es un éxito del equipo, sin duda, pero también de la arquitectura de Silverlight.

Próximamente tendremos que empezar a discutir detalles importantes de arquitectura a tener en cuenta para producir aplicaciones para Silverlight. Hay mucha tela para cortar al respecto, desde separación de capas, patrones de comunicación, seguridad, paradigmas de UI, etc.

martes, 12 de junio de 2007

Reflection en ASPX, eh?

Acceder a absolutamente todo mediante reflection es algo que se es poniendo cada vez mas de moda y puede traer varios problemas. Pero no voy a hablar de como solucionar los problemas sino de como usar Reflection aun mas :)

En algunos casos puede ser muy util ver que controles hay en una pagina que de mi aplicacion, por ejemplo puede servir para configurar permisos sobre controles como Toolbars o Grillas, editar informacion dinamicamente en base a WebParts de otras paginas, etc..
Cuando se crea un Toolbar en una pagina lo mas normal es cargar la informacion sobre que operaciones (botones) tiene en una base de datos manualmente, pero es aun mucho mejor si la pagina en la que se configuran esos permisos puede saber automaticamente que botones tiene cada pagina....

Lo primero que hay que hacer para obtener una pagina es crear un PageHandler, para eso lo unico necesario es la URL, por ejemplo "/Carpeta/Pagina1.aspx":


// Creo un Request Mock, realmente no se va a realizar ningun Request.
SimpleWorkerRequest request = new SimpleWorkerRequest("/Carpeta/Pagina1.aspx", string.Empty, TextWriter.Null);
HttpContext mockContext = new HttpContext(request);

// Obtengo el handler de la pagina en cuestion...
IHttpHandler handler = PageParser.GetCompiledPageInstance(virtualPath, fileName, mockContext);

// Si se quiere ya se puede leer el nombre de la clase de esa pagina :)
className = handler.GetType().BaseType.FullName;


Una vez que tenemos el HttpHandler que se encarga de procesar la pagina, lo que tenemos que hacer es procesarla. Pero primero creemos un delegado que nos va a servir para indicarle a la pagina que cree los controles que nosotros queremos dinamicamente.
Durante todo este ejemplo se obtienen controles Toolbar, que es hipoteticamente un control personalizado, pero podria ser cualquier otro tipo (incluso todos los controles).

delegate Toolbar BuildToolbarDelegate();


Ahora, el codigo que recorre todos los controles declarados y compara el tipo. Es importante notar que lo que en realidad se esta recorriendo no es la clase que nosotros creamos sino una clase que ASP.NET genera dinamicamente cuando se modifica un ASPX. Esa clase posee informacion sobre todos los controles y metodos necesitados por el runtime de .NET.



private IEnumerable<Toolbar> GetPageControls(IHttpHandler handler)
{
Type handlerType = handler.GetType();

// Busco todos los controles definidos en la pagina
FieldInfo[] fields = handlerType.GetFields(BindingFlags.NonPublic BindingFlags.Public BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
// Si el field que estoy recorriendo actualmente es un control del tipo Toolbar...
if (typeof(Toolbar).Equals(field.FieldType))
{
// Llamo a un metodo generado en runtime por ASP.NET para cargar la informacion del
// control, el metodo se llama "__BuildControl" + id_del_control
string MethodName = string.Format("__BuildControl{0}", field.Name);
MethodInfo BuildControl = handler.GetType().GetMethod(MethodName, BindingFlags.Instance BindingFlags.NonPublic);

// Forma simple y lenta de llamar a ese metodo:
//yield return (Toolbar)BuildControl.Invoke(handler, null);

// Forma rapida (aunque hay mejores aun):
BuildToolbarDelegate buildDelegate = (BuildToolbarDelegate) Delegate.CreateDelegate(
typeof (BuildToolbarDelegate),
handler,
BuildControl,
true);

Toolbar t1 = buildDelegate();
yield return t1;
}
}
}



Esta tecnica simple permite hacer muchas cosas y extender la configuracion de la aplicacion usando bastante poco codigo. Incluso si se quiesiese se podria definir los tamaños de todos los controles en runtime, y para setearlos no seria tan complicado. Al menos no tanto como si no se usase esta tecnica.

De todas formas hay que tener cuidado porque cuando se procesan varias paginas puede empezar a ser lento para el usuario, pero eso se puede solucionar con Cache...

Suerte!

Smalltalk en la plataforma .NET?

Así es. Mientras que otros proyectos como Smallscript (o S#) quedaron frizados en el tiempo, Peter Fisk realmente avanzó con su implementación de Vista Smalltalk para la plataforma Microsoft.

Vista Smalltalk está profundamente basado en Windows Presentation Foundation y actualmente Peter está trabajando fuertemente en su implementación sobre Silverlight, que avanza permamentemente como puede seguirse en su blog.

Smalltalk es un lenguaje que -en mi opinión- coincide perfectamente con la arquitectura de soluciones con clientes del tipo WPF/Silverlight basados en ambientes distribuidos, y para todos los viejos que tuvimos nuestro romance con él, es bueno verlo volver en una plataforma moderna y cercana a nuestro trabajo cotidiano.

Home page de Vista Smalltalk.

lunes, 11 de junio de 2007

Testing de Repositorios

Bueno, despues de algunos dias amenazando con que iba a hacer este post me digne a escribirlo...

Cada vez que empiezo con un nuevo proyecto me pregunto como testear mejor los repositorios, o al menos de una formas mas sencilla. Esta vez hice 2 clases bases para eso:

- BaseMemoryRepository.cs: Es una clase abstracta que tiene 2 tipos genericos (). El TEntity indica cual es la clase que se debe persistir, por ejemplo puede ser un simple Customer, el TIdentity es el tipo de Id.

Si el TIdentity especificado es del tipo numerico (Int16, 32 ó 64) o GUID, la misma clase se encarga de la generacion automatica, pero si se quiere usar algo muy complicado la unica solucion es hacer un override del metodo object GetNextId().

De esta forma, un repositorio simple quedaria asi:


public class CustomerRepository : BaseMemoryRepository,
ICustomerRepository
{
protected override int GetEntityId(Customer ent)
{
return ent.Id;
}

protected override int GetEntityId(Customer ent)
{
return ent.Id;
}

protected override void SetEntityId(Customer ent, int ident)
{
ent.Id = ident;
}
}

- CustomerRepositoryTests.cs: Esta clase es un poco mas compleja de usar que la anterior. Provee la funcionalidad para realizar CRUD todo junto, si bien no es lo mejor que se puede hacer en TDD, normalmente testear las operaciones basicas es resuelto de la misma forma. Por ejemplo si se usa un ORM lo mas probable es que funciona todo o no funciona nada.
Es importante implementar 4 metodos en esta clase, estos se encargan de:
- Crear un objeto de Testing
- Devolver el Id de una entidad, necesario por el desconocimiento de cual es la propiedad Id de un objeto
- Validar si el id de un objeto es valido; una vez que se crea un objeto se espera que un nuevo Id se le asigne, por eso con validar solamente que sea mayor a 0 alcanza en la mayoria de los casos.
- El Test :) (que tiene que llamar a base.BasicOperationsTest())

Aca va un ejemplo:

[TestFixture]
public class CustomerRepositoryTests : BaseRepositoryTests
{
public override void BuildTestEntity(out Customer e)
{
// Tengo que crear un nuevo Customer y configurar
// informacion de prueba
e = new Customer();
e.Name = "Name";
e.Gender = Gender.Male;
e.BornDate = DateTime.Now;
}

public override object GetEntityId(Customer ent)
{
return ent.Id;
}

public override bool IsValidEntityId(Customer entity)
{
// Una validacion simple
return entity.Id > 0;
}

[Test]
public override void BasicOperationsTest()
{
// Aca hago el trabajo real!
base.BasicOperationsTest();
}
}
Y listo, una vez que implementamos esas 2 clases por repositorio ya tenemos todos los test simples...

El codigo completo con los 2 ejemplos de implementacion los pueden bajar de acá o de acá

viernes, 8 de junio de 2007

Add-In de PowerShell para Reflector

El inefable KZU (léase kazu) publicó recientemente este Add-In de PowerShell para Reflector.

Disponible en el sitio de Add-Ins de Reflector.

Debugging Avanzado en .NET

Un interesante artículo de Rodolfo "Rodo" Finochietti ubicado en el blog del LATAM Architect Newsletter.

http://blogs.msdn.com/latamarchitectnewsletter/archive/2007/06/08/debugging-avanzado-en-net.aspx

Puntapié inicial

Este blog surgió como una idea de comunicación complementaria a a lista arquitectura@mug.org.ar en ún desayuno de trabajo esta mañana en la sede del MUG.

El objetivo del logs intercambiar noticias, conceptos e ideas entre participantes del grupo de Arquitectura del MUG. Cualquier miembro del grupo que tenga matrial interesante para compartir puede solicitar su habilitación como autor en la lista.

Esperamos que el recurso sea un buen complemento a la actividad de debate tradicional de la lista de correos.