PRISM y Winforms

Los que sigais mi blog ya habreis visto que últimamente comento algunas cosillas sobre PRISM, la librería para crear aplicaciones compuestas en WPF.

En este post, pero no quiero hablar de PRISM y WPF, sino sobre si es posible aprovechar PRISM para la creación de aplicaciones compuestas usando Winforms. Recordad que en Winforms ya tenemos una solución completa para la creación de aplicaciones compuestas: CAB  y SCSF. Ezequiel publicó hace tiempo un post lleno de enlaces sobre CAB y SCSF. Echadle una ojeada si os interesa el tema.

Aunque tengamos CAB y SCSF para crear nuestras aplicaciones compuestas en Winforms es lícito que nos planteemos si PRISM es una solución que se adapta a nuestras necesidades: Es más ligero que CAB y sus partes están menos acopladas. P.ej. si usamos CAB estamos atados forzosamente al ObjectBuilder, mientras que con PRISM el contenedor IoC que queramos usar lo podemos escojer. Además, los que hayan programado en CAB sabrán que tiene algunos comportamientos extraños, que aumentan mucho su curva de aprendizaje… si a todo esto le sumamos que PRISM es la futura guia de aplicaciones composite, quizá nos interese ver si podemos empezar a aplicarla ya en nuestras aplicaciones Winforms.

Voy a intentar dar una respuesta en este post, pero antes que nada un disclaimer: las aplicaciones winforms que creemos usando PRISM tendrán dependencias a assemblies de WPF, por lo que serán aplicaciones winforms sólo para el framework 3. Es posible usar PRISM sin ninguna dependencia a WPF, pero se pierde parte de su funcionalidad.

1. El punto de partida

El punto de partida, como muchas cosas en esta vida, la da Google. Buscando por Winforms y PRISM uno llega a un post Brian Noyes: Composite Extensions for Windows Forms. En este post Brian comenta el uso básico de PRISM para Winforms. Partiremos de su post y lo extendremos un poco.

Asumo que os habeis leído su post, y que os habeis descargado de su blog el proyecto. Si no, yo he puesto una copia tal cual (ver enlaces al final del post). Para compilar el proyecto de Brian, primero debeis compilar la solución CompositeApplicationLibrary.sln (dentro del directorio CAL), para generar PRISM. Para poder compilarlos necesitareis tener los assemblies de Unity (Microsoft.Practices.Unity.dll y Microsoft.Practices.ObjectBuilder2.dll). Unity os lo podeis descargar de la página de Unity en Codeplex.

Una vez tengais los assemblies de PRISM generados podeis abrir la solución CompositeExtensions.sln y usais los assemblies de PRISM para colocar las referencias que falten. Compilais la solución y ya tendreis las extensiones de Brian para PRISM.

2. Explorando el punto de partida

Las extensiones de Brian para PRISM, generan dos assembiles nuevos (más dos adicionales de tests unitarios):

  1. CompositeExtensions.Unity.dll: Contiene un bootstrapper nuevo (SimpleUnityBootstrapper) que nos permite crear aplicaciones winforms.
  2. CompositeExtensions.dll: Contiene un port a winforms del sistema de eventos de PRISM.

Ambas extensiones están totalmente libres de cualquier referencia a WPF. Para desarrollar una aplicación con las extensiones de Brian, simplemente es necesario crear un Bootstrapper derivado de SimpleUnityBootstrapper, registrar en Unity el control que queramos utilizar como “contenedor” de nuestras vistas, y en los módulos recuperar el  control “contenedor” y añadir en él los controles hijos. El post de Brian lo explica paso por paso y hay una aplicación de demo (muy simple) que os recomiendo que la mireis bien. Porqué ahora vamos a empezar a extender el trabajo de Brian.

3. Añadiendo soporte para regiones

Las extensiones de Brian están bien, pero no hay soporte para el concepto de regiones… Supongo que es deliberado, ya que las interfaces y clases que debemos usar tienen dependencias contra WPF, pero supongamos que eso no nos importa y vayamos a ver como añadir soporte para regiones windows forms en PRISM.

3.1. Nuestro Region Adapter

La clave es tener nuestro propio RegionAdapter que sea capaz de interaccionar con un contenedor Winforms (en este caso un Control). Crear un RegionAdapter es muuuuy fácil: basta con derivar de la clase RegionAdapter<T> (siendo T el tipo de contenedor) y redefinir CreateRegion() y Adapt(). En el primer método debemos devolver el tipo de region PRISM que queremos. En el segundo debemos “adaptar” los contenidos de la región al contenedor usado.

Veamos el código y estará todo mucho más claro:

public class ControlRegionAdapter : RegionAdapterBase<Control>
{
    protected override IRegion CreateRegion()
    {
        return new AllActiveRegion();
    }
    protected override void Adapt(IRegion region, Control regionTarget)
    {
        region.ActiveViews.CollectionChanged += delegate
        {
            regionTarget.Controls.Clear();
            foreach (object co in region.ActiveViews)
            {
                if (co is Control)
                {
                    regionTarget.Controls.Add((Control)co);
                }
            }
        };
    }
}

En el método CreateRegion devolvemos una AllActiveRegion, que es una región de PRISM que entiende que todas sus vistas son activas.

En el método Adapt, hacemos que cada vez que cambie la colección de ActiveViews de la región, nos coloque las vistas activas dentro de la colección Controls del contenedor. En este punto (al usar CollectionChanged) es cuando nos aparece la referencia contra el assembly de WPF WindowsBase.dll.

3.2 Indicar a PRISM que use nuestro nuevo Region Adapter

Para indicar a PRISM que use un Region Adapter deteminado, debemos usar la clase RegionAdapterMappings, y añadir el mapping correspondiente. Un mapping le indica a PRISM que RegionAdapter usar para cada tipo de contenedor de región.

En este punto deberemos modificar las extensiones de Brian: él no usa el concepto de regiones, así que no crea ningún RegionAdpaterMappings inicial. Para hacerlo deberemos modificar la clase SimpleUnityBootstrapper. En el método ConfigureContainer, dentro del if (_useDefaultConfiguration) añadimos la línea:

RegisterTypeIfMissing(typeof(RegionAdapterMappings),
typeof(RegionAdapterMappings), true);

Con esto hacemos que al crear el contenedor Unity, se cree un singleton de tipo RegionAdapterMappings. La clase RegionAdapterMappings está dentro del assembly Microsoft.Practices.Composite.Wpf.dll de PRISM, por lo que debereis añadir la referencia.

El siguiente paso es añadir un método protected virtual en la misma clase SimpleUnityBootstrapper:

protected virtual RegionAdapterMappings ConfigureMappings() 
{
    return Container.Resolve<RegionAdapterMappings>();
}

Y finalmente lo llamamos desde el método Run del propio SimpleUnityBootstrapper. Justo después de la llamada a ConfigureContainer(), llamamos a ConfigureMappings(). Con ello hemos modificado el bootstrapper inicial de Brian para que cree un RegionAdapterMappings (vacío) y nos de un punto de extensión (ConfigureMappings) donde nosotros podamos añadir nuestros propios mappings.

Ahora en nuestra clase bootstrapper podemos hacer un override del método ConfigureMappings y añadir nuestro mapping para que use la clase ControlRegionAdapter que hemos definido antes:

protected override RegionAdapterMappings ConfigureMappings()
{
    RegionAdapterMappings mappings = base.ConfigureMappings();
    mappings.RegisterMapping(typeof(Control), new ControlRegionAdapter());
    return mappings;
}

Con esto indicamos a PRISM que use nuestro Region Adapter cuando añadamos elementos a una región cuyo contenedor sea un Control.

3.3 Crear el RegionManager

Para poder crear Regiones, necesitamos crear un RegionManager. Para ello, lo más fácil és modificar, de nuevo, el SimpleUnityBootstrapper de Brian para que nos cree un RegionManager. Otra vez dentro del método ConfigureContainer, dentro del mismo if (_useDefaultConfiguration) añadimos la línea para que nos registre el RegionManager:

RegisterTypeIfMissing(typeof(IRegionManager), 
typeof(RegionManager), true);

Ahora ya tenemos un RegionManager de PRISM listo para usar… Ya sólo nos queda crear una región.

3.4 Crear una región

En WPF se pueden definir las regiones usando XAML, pero en winforms no tenemos nada parecido, así que vamos a hacerlo programáticamente. Por suerte el RegionManager tiene un método AttachNewRegion que nos va a servir para ello.

Primero, modificaremos de nuevo el SimpleUnityBootstrapper de Brian para añadir un método virtual:

protected virtual void AttachInitialRegions() { }

Y lo llamamos desde el método Run, justo después de la llamada a ConfigureMappings que añadimos antes.

Ahora, podemos volver a nuestro bootstrapper y hacer el override para crear la región inicial:

protected override void AttachInitialRegions()
{
    base.AttachInitialRegions();
    this.Container.Resolve<IRegionManager>().
        AttachNewRegion(this.Shell.MainRegionContainer,"Main");
}

Asumid que this.Shell es una referencia al formulario principal de la aplicación y que la propiedad MainRegionContainer me devuelve un Control que es el contenedor de la región.

4. Un módulo… para hacer algo

Las aplicaciones PRISM se componen de módulos (a la práctica objectos que implementan IModule) que colaboran entre ellos. Cada módulo se encarga de implementar parte de la aplicación, generalmente añadiendo vistas a las regiones existentes o bien añadiendo regiones nuevas.

Vamos a crear un módulo realmente simple, que añada una vista a nuestra región.

Para ello, añadimos una clase nueva que implemente IModule. El código puede ser como el siguiente:

public class Module1 : IModule
{
    private IUnityContainer container;
    private IRegionManager regionManager;

    public Module1(IUnityContainer ctl, IRegionManager rm)
    {
        this.container = ctl;
        this.regionManager = rm;
    }

    public void Initialize()
    {
        this.RegisterViewsAndServices();
        this.regionManager.Regions["Main"].
            Add(container.Resolve<IView1>());
    }

    private void RegisterViewsAndServices()
    {
        this.container.RegisterType<IView1, View1>();
    }
}

En el constructor recibimos el contenedor Unity y el RegionManager a usar. Los módulos los crea PRISM y ambos parámetros del constructor son suministrados via Dependency Injection por Unity.

El método Initialize() es el único método que declara IModule, y es donde tenemos que hacer todo el trabajo. En este caso llamamos a un método propio (RegisterViewsAndServices) que indica que cuando alguien pida un objeto IView1, devuelva un View1. Finalmente, accedemos al RegionManager y añadimos una instancia nueva de IView1.

Que son IView1  y View1? IView1 es una interfaz, los métodos de la cual no tienen importancia (de hecho, en mi ejemplo está vacía). View1 es la vista a añadir: un UserControl cuyo contenido puede ser el que se desee.

Finalmente sólo queda hacer que nuestro bootstrapper cargue el módulo. Para ello usamos el StaticModuleEnumerator para cargar e inicializar el módulo:

protected override IModuleEnumerator GetModuleEnumerator()
{
    return new StaticModuleEnumerator().
        AddModule(typeof(Module1));
}

Y listos! Con ello nuestra aplicación PRISM funcionando en Windows Forms está lista!

Aquí os dejo el siguiente código de ejemplo:

  1. Extensiones de Brian Noyes originales.
  2. Modificaciones a las extensiones de Brian Noyes (no incluye ni su aplicación de demo, ni tests ni el código de PRISM).
  3. Aplicación PRISM en Windows Forms usando regiones.

¡Espero que os sirva!

Saludos!

Gestionando las dependencias entre módulos cargados on-demand en PRISM

Una aplicación PRISM se compone de varios módulos que colaboran entre ellos. Un módulo PRISM simplemente es un objeto que implementa la interfaz IModule. En un mismo assembly pueden haber tantos módulos PRISM como se desee.

PRISM ofrece dos métodos para la carga de los módulos: O bien se cargan todos al principio de la aplicación, o bien se cargan on-demand (es decir, cuando se necesitan). La primera opción es la más simple, pero en algunos casos nos interesa ir cargando los módulos cuando se necesiten (bien porque hay muchos módulos posibles o bien porque la inicialización de estos módulos es un poco pesada).

Igualmente los módulos pueden tener dependencias entre ellos: si el módulo A depende del módulo B, indica que el módulo B debe estar cargado cuando se cargue el módulo A. En caso contrario PRISM lanzará una excepción con el mensaje “A module declared a dependency on another module which is not declared to be loaded”.

Los módulos se cargan mediante dos clases: el IModuleEnumerator, que enumera los módulos existentes y el IModuleLoader que los carga e inicializa. Existen varias implementaciones de esta clases y nosotros nos podemos crear las nuestras. En función del IModuleEnumerator que usemos debemos usar un mecanismo u otro para indicar que el módulo no se carga por defecto, sino que se cargará on-demand. P.ej. si usamos el DirectoryLookupModuleEnumerator (que enumera todos los módulos de todos los assemblies de un directorio en particular), debemos decorar el módulo con el atributo Module con la propiedad StartupLoaded a false:

[Module(ModuleName = ModuleNames.MARKETPLACE_MODULE, 
StartupLoaded = false)] public class MarketModule : IModule { }

Para cargar un módulo on-demand debemos obtenerlo mediante el IModuleEnumerator y cargarlo mediante el IModuleLoader (usando el método Initialize):

ModuleInfo mi = Container.Resolve<IModuleEnumerator>().
GetModule(ModuleNames.MARKETPLACE_MODULE); Container.Resolve<IModuleLoader>().Initialize(new ModuleInfo[] { mi });

(Container es la propiedad que me da acceso al contenedor IoC usado, que me permite obtener el IModuleEnumerator y el IModuleLoader).

De forma similar a como indicamos que un módulo se cargará on-demand podemos especificar que un módulo depende de otro. La forma exacta de hacerlo depende de nuevo del IModuleEnumerator usado. Si usamos el DirectoryLookupModuleEnumerator debemos decorar la clase módulo con el atributo ModuleDependency indicando de que módulo depende dicho módulo:

[Module(ModuleName = ModuleNames.MARKETPLACE_MODULE, 
StartupLoaded = false)] [ModuleDependency(ModuleNames.CARDS_MODULE)] public class MarketModule : IModule { }

El módulo MARKETPLACE_MODULE depende del módulo CARDS_MODULE: el segundo debe estar cargado antes de cargar el primero. Así pues es de esperar que el IModuleLoader cuando cargue el módulo MARKETPLACE_MODULE cargue también el módulo CARDS_MODULE…

… pues no. El IModuleLoader no cargará automáticamente el módulo CARDS_MODULE, en su lugar si no está cargado lanzará la excepción previamente comentada.

Vosotros debeis saber que dependencias tiene cada módulo y aseguraros que cada módulo está cargado. Es decir, en mi caso yo debo cargar CARDS_MODULE antes que MARKETPLACE_MODULE, o como muy tarde a la vez:

ModuleInfo mc = Container.Resolve<IModuleEnumerator>().
GetModule(ModuleNames.CARDS_MODULE); ModuleInfo mm = Container.Resolve<IModuleEnumerator>().
GetModule(ModuleNames.MARKETPLACE_MODULE); Container.Resolve<IModuleLoader>().
Initialize(new ModuleInfo[] { mc, mm });

Por suerte es posible un workaround para no tener que ir buscando todas las dependencias: hacerse un método de extensión sobre IModuleEnumerator, y que devuelva un array de ModuleInfo: el módulo junto con todas sus dependencias:

namespace Microsoft.Practices.Composite.Modularity
{
    public static class ModuleEnumeratorExtensions
    {
        public static ModuleInfo[] GetModuleWithDependencies(
this IModuleEnumerator moduleEnumerator, string moduleName) { List<ModuleInfo> moduleInfoList = new List<ModuleInfo>(); ModuleInfo module = moduleEnumerator.GetModule(moduleName); moduleInfoList.Add(module); if (module.DependsOn != null) { foreach (string dependencyName in module.DependsOn) { if (!moduleInfoList.Exists(
existingModule => existingModule.ModuleName ==
dependencyName)) { moduleInfoList.AddRange(
GetModuleWithDependencies(
moduleEnumerator, dependencyName)); } } } return moduleInfoList.ToArray(); } } }

Y para cargar un módulo junto con todas sus dependencias:

ModuleInfo[] mis = Container.Resolve<IModuleEnumerator>().
GetModuleWithDependencies(ModuleNames.MARKETPLACE_MODULE);
Container.Resolve<IModuleLoader>().Initialize(mis);

Es una de esas cosas que uno se pregunta porque no lo habrán añadido de serie… 🙂

Nota: La información de este post está sacada de este post de Mariano Converti. Como es habitual, todo el mérito para él… Su blog sobre PRISM es de imprescindible consulta!

PRISM y AvalonDock

Hola a todos!

Conocéis PRISM? Viene a ser, salvando las distancias, la CAB de WPF: es decir un conjunto de buenas prácticas para la creación de aplicaciones compuestas en WPF y una librería que implementa dichas buenas prácticas. Si desarrollais aplicaciones en WPF es obligatorio echarle un vistazo. Pasaos por la página de PRISM en codeplex.

Por otro lado, AvalonDock es una muy buena librería que proporciona soporte para interfaces dockables usando WPF que simula al estilo de docking de Visual Studio.

Estoy desarrollando una aplicación usando ambas librerías y me he encontrado con un problemilla: al añadir una vista usando PRISM dentro de un contenedor de AvalonDock aparece un error. A ver, que me explico un poco mejor… 🙂

PRISM usa el concepto de “regiones” para dividir el espacio de la ventana de la aplicación. En cada “región” se pueden incrustar una o más vistas (objetos que se representan visualmente). Por ejemplo podemos mapear una región de PRISM a un ItemsControl y cada vista que añadamos aparecerá dentro de este ItemsControl. Para mapear una región PRISM a un control se usa XAML:

<ItemsControl Grid.Row="0" cal:RegionManager.RegionName="HelpZone" />

Por su lado AvalonDock se basa en proporcionar un contenedor especial (el DockingManager) dentro del cual se insertan otros contenedores especiales que contienen el contenido a mostrar… el cual tiene que ser un objeto de unas clases especiales, llamadas DockableContent o DocumentContent. Estas clases son las que realmente contienen el contenido real. P.ej. para mostrar una cadena dentro de una ventana dockable usando AvalonDock necesito el siguiente código XAML:

<ad:DockingManager Name="mainDockingManager" Grid.Row="1">
  <ad:DocumentPane>
     <ad:DockableContent>Hola AvalonDock</ad:DockableContent>
  </ad:DocumentPane>
</ad:DockingManager>

La clase DocumentPane contiene tantos DockableContent como ventanas dockables se quieran tener.

Al mezclar PRISM y AvalonDock es cuando surgen los primeros problemas. Si definimos una región dentro del DocumentPane:

<ad:DockingManager Name="mainDockingManager" Grid.Row="1">
  <ad:DocumentPane cal:RegionManager.RegionName="MainZone" />
</ad:DockingManager>

 

Cuando añadamos una vista, no dará una excepción: DocumentPane can contain only DockableContents or DocumentContents!

Esto ocurre porque PRISM intenta asociar como contenido del DocumentPane la vista que directamente le hemos indicado, que será un UserControl o algún otro objeto pero no un DockableContent o un DocumentContent.

Lo que hemos de conseguir es que PRISM, de forma transparente para nosotros, nos cree un DockableContent (o un DocumentContent) y nos lo añada al contenido del DocumentPane al cual está mapeado nuestra región de PRISM. Por suerte,  en PRISM tenemos el concepto de RegionAdapter, que como su nombre indica es una clase que “adapta” los contenidos de una región de PRISM al contenedor real (nuestro DocumentPane). Vamos a ver como podemos implementar un RegionAdapter para DocumentPane.

El código quedaría más o menos así:

public class DockableRegionAdapter : RegionAdapterBase<DocumentPane>
{
    protected override IRegion CreateRegion()
    {
        return new AllActiveRegion();
    }

    protected override void Adapt(IRegion region, 
DocumentPane regionTarget) { region.ActiveViews.CollectionChanged += delegate { var childs = new Dictionary<object, DockableContent> (); foreach (var child in regionTarget.Items) { if (child is DockableContent) { childs.Add(((DockableContent)child).Content, (DockableContent)child); } } regionTarget.Items.Clear(); foreach (object ci in region.Views) { DockableContent dc = childs.ContainsKey(ci)
? childs[ci] : new DockableContent() { Content = ci }; regionTarget.Items.Add(dc); } }; } }

Los dos métodos que hay que redefinir cuando se crea un RegioAdapter de PRISM son CreateRegion y Adapt. En el primero simplemente hemos de devolver el tipo de región que queremos. En este caso simplemente creo un objeto AllActiveRegion, que es un región de PRISM que todas las vistas que tenga las considera activas.

El segundo método es Adapt, y es donde se hace todo el trabajo. Cada vez que cambie la colección ActiveViews, básicamente borro el contenido del DocumentPane de AvalonDock y lo añado de nuevo, creando un DockableContent nuevo para cada vista que haya en la región. El código que rellena el diccionario childs es para reutilizar aquellos DockableContent que se hubiesen creado anteriormente.

Finalmente solo nos queda informar a PRISM que tenemos un RegionAdapter nuevo. Para ello redefinimos el método ConfigureRegionAdapterMappings del Bootstrapper para añadir nuestro RegionAdapter vinculado a contenedores de tipo DocumentPane:

protected override RegionAdapterMappings 
    ConfigureRegionAdapterMappings()
{
    RegionAdapterMappings mappings = 
        base.ConfigureRegionAdapterMappings();
    mappings.RegisterMapping(typeof(DocumentPane), 
        new DockableRegionAdapter());
    return mappings;
}

Y listos! Ahora al añadir una vista a la región de PRISM, se crea automáticamente un DockableContent y se añade al DocumentPane que contiene la región, lo que añade una ventana dockable con la nueva vista en la interfaz de usuario.

No puedo asegurar que sea la mejor implementación posible pero… a mi me funciona 😉

¡Bien por PRISM!

[WPF] Databinding con un PasswordBox

Hola! ¿Que tal os sienta el 2009? Espero que lo mejor posible 🙂

Hoy un post cortito para comentar un problemilla y su solución.

El problemilla es que al intentar realizar DataBinding desde un PasswordBox no funciona, porque la propiedad Password, no es una DependencyProperty.

Es decir, mientras que esto funciona y liga la propiedad Text a la propiedad Login del DataContext:

<TextBox Grid.Column="1" x:Name="txtLogin"  VerticalAlignment="Center"
Text="{Binding Login}" />

esto no funciona:

<PasswordBox Grid.Row="1" Grid.Column="1" x:Name="txtPassword" VerticalAlignment="Center" 
Password="{Binding Password}" />

Ya que la propiedad Password no es una DependencyProperty.

¿La solución? Añadir una propiedad enlazada que sí que sea una DependencyProperty y que tenga el mismo valor que la propiedad Password.

La solución completa la podeis encontrar en este post de Functional Fun: WPF PasswordBox and Data binding. Todo el mérito es suyo, yo sólo comparto el post, puesto que me ha parecido muy interesante.

¡Saludos!

Una dudilla sobre C#

Hola… a punto todos para comernos las uvas????

Antes de que lo hagáis y os lanceis luego a brindar con cava por el nuevo año, y una cosa lleve a la otra y no esteis en condiciones, digamos de… pensar mucho, a ver si alguien me sabe responder una dudilla que me ha surgido hoy.

¿Porque este código no compila?

public class Foo
{
    public string Name { get { return string.Empty; } }
    public string Name() { return string.Empty ; }
}

Por si alguien (como yo) se pensaba que eso compilaba, pues no. Visual Studio se queja con un claro y explícito error CS0102: The type ‘ConsoleApplication232.Foo’ already contains a definition for ‘Name’.

Alguien sabe el porque de esta limitación? Es decir, porque han evitado que podamos hacer esto? Alguien tiene alguna idea?

Epa!!! Buen año a tod@s y que el 2009 os sea lo más propicio posible!!!! Y no os comáis las uvas antes de tiempo! Recordad que el último minuto de este 2008 tiene un segundillo de más!!! xD

Mi carta a los reyes…

Aunque sería para unos reyes que pasaran más allá del 2010, pero bueno… por pedir que no quede 😛

Estas son las cosas que me gustaría que algun dia se incorporasen a C#. Como son fiestas mágicas, pues aquí van para ver si los chicos de Redmond se animan y para algún milenio las tenemos…

Pongo ideas al margen de si son posibles/factibles con el CLR actual… simplemente son cosas que me gustarían que estuviesen en el lenguaje. Solo pongo las ideas y un ejemplo de lo que me gustaría, sin desarrollarlas a fondo… 🙂

1. Conceptos en genéricos

La idea es poder representar como restricción de un tipo genérico algo más allá que una interfaz. Por ejemplo:

concept HasHandle
{
    IntPtr Handle{ get; }        
}

class Foo
{
    public static void Main(string[] args)
    {
        new Foo().Bar(new Form());
    }

    public void Bar<T> (T t)
        where T : HasHandle
        {
            DoWork(t.Handle);
        }
}

Este código deberia compilar porque el concepto HasHandle define una propiedad llamada Handle de tipo IntPtr. El mètode genérico Bar<T> utiliza esta propiedad. Y la llamada a Bar con un paramétro Form satisface el concepto, puesto que la clase Form tiene una propiedad IntPtr llamada Handle.

Los conceptos deberían poder ser genéricos a su vez:

concept HasXXX<T>
{
    T XXX{ get; set;}        
}

class Foo
{
    public void Bar<T,U> (T t)
        where T : HasXXX<U>
        {
            U u = t.XXX;
        }
}

El método Bar<T,U> debería poder llamarse con cualquier tipo T que tuviese una propiedad U XXX {get; set;}

Los conceptos deberían poder hacer referencia a la existencia de métodos estáticos:

concept Comparable<T>
{
    bool operator > (T t1, T t2);
}

class Foo
{
    void Bar<T> (IEnumerable<T> items)
        where T : Comparable<T>
    {
    }
}

El método Bar<T> debería poder llamarse con cualquier tipo T que tuviese definido el operador ‘>’ (aunque este esté definido de forma estática).

2. Constantes binarias

Pues eso mismo…

int i = 0b00001100;

Fácil, no??

3. Tipos anónimos que puedan implementar una interfaz

Los tipos anónimos tal y como estan ahora sirven para poco más que para LINQ. Sería interesante que pudiesen implementar una interfaz y que pudiesen ser devueltos a través de referencias a dicha interfaz:

interface ISomeInterface
{
    int Foo(int);
}

class Bar
{
    ISomeInterface Baz()
    {
        return new ISomeInterface  { void Foo(int i) { return i+1;} };
    }
}

El tipo anónimo creado en el método Baz, implementa la interfaz ISomeInterface y es devuelto a través de una referencia a dicha interfaz.

4. Referencias const

Al estilo de C++. Mira que esto es simple y útil y no se porque no lo han metido… Para más info: http://www.cprogramming.com/tutorial/const_correctness.html.

5. Enumeraciones con cualquier tipo

Estaría bien que los enums pudieran tener cualquier tipo base:

class Planeta
{
    long Diametro { get; set; }
    long DistanciaSol { get; set; }
}
enum SistemaSolar<Planeta>
{
    Mercurio = new Planeta() { Diametro=4879, DistanciaSol=46000000},
    Tierra = new Planeta() { Diametro=12742, DistanciaSol=150000000}
}
class Foo()
{
    Bar () { this.Baz(SistemaSolar.Mercurio);}
    Baz(SistemaSolar p)
    {
        if (p.Diametro > 10000) { ... }
    }
}

El enum SistemaSolar se define como una enumeración de objetos de la clase Planeta.

El método Foo.Baz acepta como parámetro sólo SistemaSolar.Mercurio o SistemaSolar.Tierra. Pasarle cualquier otro objeto Planeta no debería compilar.

Felices fiestaaaaaaaaaaaaaaaaaaaaaaas!

Bueno… paro ya de pedir cosas, jejeeee… 🙂

Solo desearos a todos los geeks unas felices fiestas, llenas de alegría, familia, ceros y unos…

… y que el tió os cague muchos regaloooooooooooooooooooooooooooooooos!!!!

Duda metafísica sobre “contravarianza” en delegates

Hola… hoy voy a poner un post sobre una dudilla metafísica que me ha surgido, concretamente relativa a los delegates. Y he pensado… que mejor sitio que ponerla que aquí??? 😉

Los delegates en C# 2.0 son contravariantes, es decir un delegate a un método que espera un parámetro tipo X aceptará cualquier método que espere un parámetro de cualquier tipo base de X.

Es decir, el siguiente código funciona bien:

delegate void Foo(Derived d);
public class Base { }
public class Derived : Base { }
public class SomeCode
{
    SomeCode()
    {
        Foo foo = new Foo(this.SomeMethod);
    }
    private void SomeMethod(Base b) { }
}

Todos entendemos la lógica que hay tras ello: al ser todos los objetos Derived, objetos Base, es evidente que cualquier método que trate con objetos Base, lo podrá hacer con objetos Derived, y por ello el método SomeMethod es un destino bueno para el delegate Foo.

Ahora bien, imaginemos que tenemos el siguiente código:

delegate void Foo(Derived d);
public class Base { }
public class Derived 
{
    public static implicit operator Base(Derived d) { return new Base(); }
}
public class SomeCode
{
    SomeCode()
    {
        Foo foo = new Foo(this.SomeMethod);
    }
    private void SomeMethod(Base b) { }
}

En este caso el código no compila, el compilador se queja con un No overload for ‘SomeCode.SomeMethod(Base)’ matches delegate ‘Foo’.

La duda metafísica es… creeis que debería compilar? En cierto (lo pongo en cursiva) todos los objetos Derived tambien son objetos Base, puesto que hay definida una conversión implícita de Derived a Base… con lo que podríamos entender que hay una cierta contravarianza.

O creeis que no? Que ya está bien que no compile puesto que el operador de conversión no puede representar nunca una relación is-a y por lo tanto la contravarianza no tiene sentido…

MMmmm… yo reconozco que no estoy 100% posicionado a favor de ninguna opción…

Saludos!

El valor de una certificación.

Bueno, aviso: estoy cabreado… y este post será polémico. Quien avisa no es traidor.

Estas últimas semanas me he sacado dos MCPs, en concreto el 70-529 y el 70-549, es decir el MCTS y el MCPD de aplicaciones distribuídas. No voy a hablar sobre si son fáciles o difíciles o si se ajustan a lo que realmente uno se encuentra en el mundo real, no… quiero exponer algo que ya hace tiempo me preocupa y me mosquea a partes iguales. Antes de nada he de decir que no tenía muy claro si escribir este post. Por hastío básicamente… lo digo porque lo que escribiré ahora es, lamentablemente, muy parecido a lo que escribí en mi antiguo blog en la casi-extinta clearscreen (un tick de silencio). Aquí dejo el enlace al post que escribí hará casi tres años, por si a alguien le apetece leerlo.

Antes de hacer ambos exámenes, eché una ojeada a esos pdfs que tienen preguntas parecidas a las del exámen, junto con sus respuestas (¿no hace falta poner nombres, verdad?). Lo que vi fue descorazonador… bueno, lo hubiese sido si no supiera de que va el tema, vamos… ahora ya empieza a resbalarme todo…

El caso del 70-529 es flagrante: todas (y todas significa todas) las preguntas exámen estaban en el pdf. Además éste estaba bastante bien (había algunas preguntas que creo que estaban mal y algunas otras que estaban mal seguro) o sea que alguien simplemente empollándose el pdf se sacaba perfectamente el exámen.

El caso del 70-549 no fue tan brutal. De las 60 preguntas que tenia el exámen había unas 45 que estaban en el pdf (ya veis, aunque no tan brutal sigue siendo lamentable). Eso sí, al menos el pdf del 70-549 estaba un poco peor (tenia más respuestas incorrectas)… aun así, empollándoselo uno aprueba el exámen.

No quiero ni voy a criticar a quien se empolla estos pdfs para sacarse MCPs… quiero criticar a quien permite explícitamente que esto siga así. A quien en mi opinión es el máximo culpable (aunque sea por inacción). Es decir, a Microsoft. Creía que la certificación servía para acreditar unos determinados conocimientos, pero veo que simplemente es otra patética herramienta para hacer dinero. Da igual que la certificación esté tan prostituída que los exámenes se encuentren en internet, mientras la gente siga pagando para hacerlos (donde digo gente también se puede leer empresas que quieran ser pon-aquí-tu-material-preferido certified partner). Anda que no sería fácil evitar estos pdfs teniendo un pool de centenares de preguntas por exámen en lugar de menos de 80 (el pdf del 70-529 tenia menos de 80 preguntas y repito: todas me salieron en el exámen). Y también ir cambiando las preguntas cada cierto tiempo ayudaría. ¿Cuanto le costaría hacer todo esto a Microsoft? ¿Por que no lo hace? Y más preguntas… ¿cómo es posible que determinadas academias ofrezcan un cursillo de pongamos 50 horas tras el cual garanticen tener MCPD? ¿No se suponía que estas certificaciones eran para gente con determinada experiencia? ¿50 horas en un cursillo es esta experiencia?

De ahí el título del post: ¿cuál es el valor de una certificación cuyos exámenes se encuentran (con respuestas incluídas) en internet? ¿Porque debo dedicar esfuerzo y dinero (esos exámenes no son gratis) en sacarme estas certificaciones? ¿Debo seguir jugando a este juego? ¿Hará algo al respecto Microsoft? (esta última pregunta es retórica, lo aclaro por si acaso).

¿Alguna respuesta convincente?

[Depuración] Generación programática de mini dumps

Bueno… No voy a hablar sobre como fue el DevCamp de este pasado fin de semana en puesto que ya lo han hecho Jordi Nuñez y José Manuel Alarcon. Compartimos buenos momentos, buenos cubatas y buenas charlas.

De todas las charlas que se dieron, me interesa comentar especialmente la que dio Pablo Alvarez, una charla impresionante bajo el título de depurando hasta la saciedad. Aunque contó con apenas 45 minutos destripó a base de bien WinDbg y comentó los distintos escenarios de depuración, especialmente la depuración post-mortem (esto es, cuando el proceso ya ha reventado). Leeros su post y el ppt porque es imprescindible.

¿Y qué quiero comentar yo de la charla de Pablo? Pues bien, en la charla quedó claro que antes de meterse a depurar uno debe tener unos buenos… símbolos. Y no sólo eso sino que evidentemente es necesario tener el dump de la aplicación que ha reventado. Comentó distintas herramientas para generar este dump, pero a mí me interesa comentar un escenario que suele ser muy habitual cuando ponemos aplicaciones en producción: que sea la propia aplicación quien genere este dump cuando se produce un error. En un proyecto donde estoy trabajando tenemos nuestra aplicación desplegada en distintos clientes, y aunque simplemente con el log de excepciones managed cubrimos la mayoría de errores, en algunos casos necesitamos más información. Es ahí donde tener un dump de la aplicación nos es realmente útil.

Bueno… vamos a ver dos maneras de generar un dump para depuración post-mortem. La fácil y la no tan fácil.

La fácil

Os vais a la página de ClrDump y os lo descargáis. ClrDump es básicamente una DLL que podéis llamar desde vuestras aplicaciones .NET para generar dumps. En la propia página encontrareis la información de los métodos y sus firmas para P/Invoke.

P.ej. el siguiente código genera un dump completo cuando se produzca cualquier excepción (managed o nativa) no capturada:

SetFilterOptions(CLRDMP_OPT_CALLDEFAULTHANDLER);

string minidumpFile = string.Format(@”c:temppostmortem-{0}.dmp”, DateTime.Now.ToString(“dd-MM-yyyy”));

RegisterFilter(minidumpFile, MiniDumpWithFullMemory);

 

Las constantes y las firmas P/Invoke serian:

[DllImport(“clrdump.dll”, CharSet = CharSet.Unicode, SetLastError = true)]

private
static
extern
int RegisterFilter(string FileName, int DumpType);

[DllImport(“clrdump.dll”)]

static
extern
Int32 SetFilterOptions(int Options);

private
const
int CLRDMP_OPT_CALLDEFAULTHANDLER = 0x1;

private
const
int MiniDumpWithFullMemory = 0x2;

 

Cuando vuestra aplicación reviente tendréis vuestro minidump (en este caso será bastante grande puesto que volcará toda la memoria del proceso lo que puede dar lugar con facilidad a dumps de más de 100 ó 200 MB).

La no tan fácil

Bueno… ClrDump funciona realmente bien y no se me ocurre ninguna razón para no usarlo, excepto que os guste investigar un poco como funciona la generación de dumps… Si queréis podéis implementaros vuestra propia librería para generar dumps. Para ello nada mejor que C++, la librería dbghelp.dll y para adelante!

Dbghelp.dll es la librería que contiene, entre otras, las funciones de generación de dumps. Aunque Windows tiene una, lo mejor es usar la que viene con las Debugging Tools for Windows. Dado que es una librería unmanaged os recomiendo el uso de C++ (supongo que puede invocarse via p/invoke pero ni me lo he planteado, algunas funciones tienen firmas un poco complejas).

La función clave se llama MiniDumpWriteDump y si veis su información en la msdn podréis comprobar que tiene bastantes parámetros. Esta función es la que hace todo el trabajo de generar un minidump, ahora sólo nos queda saber cuándo llamarla: cuando se produzca cualquier excepción (managed o nativa) no controlada. Para ello debemos usar el método SetUnhandledExceptionFilter. Este método espera un puntero a una función que será la que deba ejecutarse cuando se produzca cualquier excepción no controlada. Es en este método cuando llamaremos a MiniDumpWriteDump.

El método SetUnhandledExceptionFilter espera un puntero a una función que reciba un LPEXCEPTION_POINTERS, eso es un puntero con los datos de la excepción que ha ocurrido. Esos datos se los debermos pasar a MiniDumpWriteDump para que pueda procesar la información.

A modo de ejemplo, he creado una clase DumpHelper, en C++ CLI que permite generar minidumps cuando se produzca una excepción no controlada. La cabecera sería así:

namespace DumpHelper {

 

    delegate LONG UnhandledExceptionFilterHandler(LPEXCEPTION_POINTERS pException);

 

    public
ref
class DumpHelper

    {

        private:

            UnhandledExceptionFilterHandler^ pManagedHandler;

            LPTOP_LEVEL_EXCEPTION_FILTER pFilter;

            LONG UnhandledExceptionFilter(LPEXCEPTION_POINTERS pException);

            void CreateMiniDump( EXCEPTION_POINTERS* pep );

            String^ name;

        public:

            BOOL SetExceptionHandler ();

            DumpHelper(String^ name);

    };

}

 

Defino un delegate (que contendrá el puntero a función) y simplemente dos funciones públicas: el constructor que espera una cadena (con el nombre de fichero a generar) y el método SetExceptionHandler para activar la generación de minidumps cuando el proceso reviente.

En la parte privada, tenemos la instancia del delegate (pManagedHandler), el puntero a función (pFilter), y dos funciones adicionales: UnhandledExceptionFilter y CreateMiniDump.

La implementación del método SetExceptionHandler es tal como sigue:

BOOL DumpHelper::SetExceptionHandler()

{

    pManagedHandler = gcnew UnhandledExceptionFilterHandler(this, &DumpHelper::UnhandledExceptionFilter);

    pFilter = reinterpret_cast<LPTOP_LEVEL_EXCEPTION_FILTER>(

        Marshal::GetFunctionPointerForDelegate (pManagedHandler).ToPointer());

    SetUnhandledExceptionFilter(pFilter);

    return TRUE;

}

 

Creamos el puntero pFilter para que apunte a la función UnhandledExceptionFilter del propio objeto DumpHelper. Para ello tenemos que hacerlo usando un delegate y conviertiendo luego el delegate a un puntero nativo usando la clase Marshal. Una vez tenemos el puntero, lo pasamos como parámetro al método de Windows SetUnhandledExceptionFilter. En este punto cuando se produzca una excepción no controlada en nuestro programa se nos llamará al método UnhandledExceptionFilter de nuestra clase DumpHelper. Este método es muy simplemente y simplemente llama a CreateMiniDump:

LONG DumpHelper::UnhandledExceptionFilter(LPEXCEPTION_POINTERS pException)

{

    CreateMiniDump(pException);           

// 0: Que se llame al manejador por defecto de Windows al finalizar

    return 0;

}

 

Finalmente el método CreateMiniDump es el que realiza todo el trabajo. Para ello llama a MiniDumpWriteDump:

void DumpHelper::CreateMiniDump( EXCEPTION_POINTERS* pep)

{


pin_ptr<const
wchar_t> fname = PtrToStringChars(name);

    HANDLE hFile = CreateFile(fname, GENERIC_READ | GENERIC_WRITE,

        FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);       

    if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) )

    {   

        MINIDUMP_EXCEPTION_INFORMATION mdei;

        mdei.ThreadId = GetCurrentThreadId();

        mdei.ExceptionPointers = pep;

        mdei.ClientPointers = FALSE;

        MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithFullMemory |

                                                 MiniDumpWithFullMemoryInfo |

                                                 MiniDumpWithHandleData |

                                                 MiniDumpWithThreadInfo |

                                                 MiniDumpWithUnloadedModules );

        BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),

            hFile, mdt, (pep != NULL) ? &mdei : NULL , NULL, NULL );

        CloseHandle( hFile );

    }
}

 

La función simplemente abre un fichero (usando CreateFile) y luego rellena los distintos parámetros de MiniDumpWriteDump para generar el minidump.

El uso de esta librería es muy simple. En vuestra aplicación simplemente añadís una referencia a ella, creáis un objeto DumpHelper y llamáis acto seguido a SetExceptionHandler. El mejor sitio para hacer esto es en vuestro método Main:

class Program

{

static void Main(string[] args)

{

DumpHelper h = new DumpHelper(“C:\temp\test.dmp”);

h.SetExceptionHandler();

// Resto de código…

}

}

 

Listos! Con esto vuestras aplicaciones ya generarán dumps cuando revienten!

Un saludo… y nos vamos viendo!

IoC o el poder de ceder el control

Hablando con colegas de profesión, me he dado cuenta de que muchos de ellos no terminan de comprender el patrón IoC o las ventajas que su uso comporta… Así que sin ánimo de sentar cátedra he decidido escribir este post, por si le sirve a alguien… J

IoC, que corresponde a las siglas de Inversion Of Control, agrupa a varios patrones que tienen en común que el flujo de ejecución del programa se invierte respecto a los métodos de programación tradicionales (wikipedia dixit). Generalmente el programador especifica las acciones (métodos) que se van llamando, pero en IoC lo que se especifican son las respuestas a determinados eventos o sucesos, dejando en manos de un elemento externo todas las acciones de control necesarias cuando lleguen estos sucesos.

Un claro ejemplo de IoC se encuentra en el modelo de eventos de Windows Forms. Cuando vinculamos una función gestora a un evento (p.ej. el Click de un Button), estamos definiendo la respuesta a un determinado evento y nos despreocupamos cuando se llamará a nuestra función gestora. Es decir, cedemos el control del flujo al framework para que sea él que llame cuando sea necesario a nuestro método.

Una de las principales ventajas de usar patrones IoC es que reducimos el acople entre una clase y las clases de las cuales depende, y eso cuando queremos utilizar por ejemplo tests unitarios es muy importante (no seré yo quien hable ahora de las ventajas de TDD cuando ya lo ha hecho Rodrigo (http://geeks.ms/blogs/rcorral/archive/2006/12/02/beneficios-y-carater-iacute-sticas-de-un-buen-test-unitario.aspx)). De los distintos patrones IoC me interesa comentar específicamente dos: Service Locator y cómo podemos usarlo en .NET. Para más adelante dejo Dependency Injection, otra forma de IoC que también es extremadamente útil.

Id a la nevera, coged una Voll-Damm o cualquier otra cervecilla y sentaos que este post es un poco largo… J

Service Locator

El objetivo de Service Locator es reducir las dependencias que tenga una clase. Imaginemos una clase, que depende de otras dos clases (p.ej. ServicioSeguridad y ServicioLogin). Podríamos tener un código tal como:

class
Client

{

static
void Main(string[] args)

{

new
Client().Run();

}

private
void Run()

{

// En este punto necesitamos objetos de las clases ServicioSeguridad y ServicioLogger


ServicioSeguridad ss = new
ServicioSeguridad();


ServicioLogger sl = new
ServicioLogger();


//…

}

}

En este punto tenemos un fuerte acople entre la clase Client y las clases ServicioSeguridad y ServicioLoggger. Seguiríamos teniendo este mismo acople incluso aunque utilizáramos interfaces porque deberíamos hacer el “new”:

IServicioSeguridad ss = new
ServicioSeguridad();

IServicioLogger sl = new
ServicioLogger();

 

Las principales desventajas de esta situación son:

  1. Las clases que implementan las dependencias (en nuestro caso ServicioSeguridad y ServicioLogger) deben estar disponibles en tiempo de compilación (no nos basta con tener solo las interfaces).
  2. El fuerte acople de la clase con sus dependencias dificulta de sobremanera su testing. Si queremos utilizar Mocks para de ServicioSeguridad o ServicioLogger vamos a tener dificultades

El patrón de ServiceLocator soluciona estos dos puntos, sustituyendo las dependencias de la clase Client por dependencias a los interfaces y a un elemento externo, que llamaremos Contenedor encargado de devolver las referencias que se le piden.

Cuando la clase necesita un objeto en concreto, lo pide al contenedor:

IServicioSeguridad ss = container.Resolve<IServicioSeguridad>(“servicioseguridad”);

IServicioLogger sl = container.Resolve<IServicioLogger>(“serviciologger”);

El método Resolve devolvería una referencia del tipo especificado en el parámetro genérico de acuerdo con un identificador. Evidentemente falta alguien que cree el contenedor y que agregue los servicios a él. Es decir, en algún sitio habrá algo como:

container = new
IoCContainer();

container.Add<IServicioSeguridad, ServicioSeguridad>(new
ServicioSeguridad(), “servicioseguridad”);

container.Add<IServicioLogger, ServicioLogger>(new
ServicioLogger(), “serviciologger”);

La gran diferencia es que esto no tiene porque estar en la clase Client. Simplemente pasándole a la clase Client una referencia al contenedor, eliminamos todas las dependencias de la clase Client con las clases que implementan los servicios. Y donde creamos el contendor? Pues depende… si estamos en nuestra aplicación, lo podemos crear en el método que inicialice la aplicación, pero si queremos probar la clase Client con tests unitarios podemos crear el contenedor en la inicialización del test…. Y lo que es mejor: rellenarlo con Mocks de los servicios!

Así, nuestro programa podría tener una clase Bootstrapper que crea el contenedor de IoC y lo inicializa con los objetos necesarios:

class
Bootstrapper

{

static
void Main(string[] args)

{

IIoCContainer container = new
IoCContainer();

container.Add<IServicioSeguridad, ServicioSeguridad>(new
ServicioSeguridad(), “servicioseguridad”);

container.Add<IServicioLogger, ServicioLogger>(new
ServicioLogger(), “serviciologger”);


new
Client(container).Run();

}

}

Pero si queremos usar tests unitarios de la clase Client, podemos cambiar los objetos por Mocks fácilmente:

[ClassInitialize]

public
static
void Init(TestContext context)

{

container = new
IoCContainer();

container.Add<IServicioSeguridad, ServicioSeguridadMock>(new
ServicioSeguridadMock(), “servicioseguridad”);

container.Add<IServicioLogger, ServicioLoggerMock>(new
ServicioLoggerMock(), “serviciologger”);

}

[TestMethod]

public
void TestMethod1()

{

Client c = new
Client(container);

c.Run(); // Este método Run usará los Mocks!

}

 

Fijaos que podemos lanzar tests unitarios sobre la clase Client, sin necesidad alguna de cambiar su código y utilizando Mocks. Además, la clase Client no tiene ninguna dependencia con las implementaciones de los servicios que utiliza, así que no es necesario ni que existan para poder crear la clase Client (sólo necesitamos las interfaces).

Unity

Aunque existen varios contenedores IoC open source para .NET, Microsoft tiene el suyo, también open source, llamado Unity (http://www.codeplex.com/unity/). Unity proporciona soporte para los patrones Service Locator y Dependency Injection.

Para que veais un poco su uso he dejado un proyecto de herramienta de línea de comandos para listar los ficheros de un directorio (vamos un dir, jejejee…. J).

La solución está dividida en varios proyectos:

  1. DemoIoC: Contiene el Bootstrapper, la clase que inicializa el contenedor de Unity, agrega la clase llamada ServicioFicheros y utiliza un objeto Cliente para realizar las acciones de la línea de comandos.
  2. Cliente: Contiene la implementación de la clase Cliente.
  3. Interfaces: Contiene las interfaces del servicio (en este caso sólo una)
  4. Implementations: Contiene las implementaciones del servicio (en este caso sólo una)
  5. Mocks: Contiene las implementaciones de los Mocks (en este caso sólo una)
  6. UnitTests: Contiene tests sobre la clase Cliente, usando un Mock del servicio ServicioFicheros.

El ejecutable (proyecto DemoIoC, assembly DemoIoC.exe) es un archivo de línea de comandos que acepta dos parámetros:

DemoIoC –l para listar todos los ficheros (del directorio actual)

DemoIoC –e:fichero.ext Indica si el fichero “fichero.ext” existe (en el directorio actual).

Si lo ejecutáis veréis que NO funciona bien: lista los ficheros correctamente, pero devuelve que un fichero existe cuando no es cierto y viceversa. Si miráis el código de la clase Cliente, encontrareis el error, ya que es obvio, pero lo bueno es que el proyecto UnitTest, testea este método de la clase Cliente, usando un Mock del ServicioFicheros y el UnitTest detecta el error. Observad lo fácil que ha sido sustituir el ServicioFicheros por un Mock sin alterar la clase Cliente, gracias al uso del patrón Service Locator!

PD: Estooooo… que me he dejado de adjuntar el código de ejemplo… Lo teneis aquí!!!

Saludos!!!

Referencias

Os dejo algunas referencias para su lectura por si os interesa profundizar un poco en el tema tratado: