Skip Ribbon Commands
Skip to main content

Carlos Merino

:

Home
Blog de Carlos Merino
July 01
Patrón Estrategia

Imaginemos un videojuego de aventuras. En el video juego aparecerán personajes jugadores y personajes no jugadores con diferentes características.

En nuestro mundo mágico imaginado habrá criaturas tales como magos, guerreros, trolls, etc…

Algunos de estos personajes podrán usar armas y otros no, algunos podrán usar magia y otros no, y algunos podrán volar y otros no. La combinación de estas habilidades dependerá de las clases que implementen las habilidades de estas criaturas fantásticas.

Para conseguir estos comportamientos podemos usar el patrón de diseño Estrategia.

El patrón Estrategia define una familia de algoritmos, encapsula cada uno y los hace intercambiables. Estrategia permite al algoritmo ser modificado independientemente de los clientes que los usan.

Para nuestro mundo mágico, estas familias de algoritmos serían familias de habilidades (volar,  usar magia, usar un arma)

En primer lugar creamos un interfaz común para cada habilidad, y cada implementación lleva su propio algoritmo que determina la forma de realizar esa habilidad.

public interface IHabilidadArma

{

   void UsarArma();

}

 

public class HabilidadArmaArco : IHabilidadArma

{

    public void UsarArma()

    {

        Console.WriteLine("Realiza un ataque con un arco lanzando una flecha");

    }

}

 

public class HabilidadArmaEspada : IHabilidadArma

{

    public void UsarArma()

    {

        Console.WriteLine("Realiza un ataque con la espada");

    }

}

 

Y así todas las habilidades e implementaciones que necesitemos.

Crearemos una clase abstracta llamada PersonajeAbstracto, en el que estarán todas las acciones comunes a todas las criaturas; por ejemplo: puntos de vida, desplazarse, display en el caso de que haya gráficos, etc. Y también habrá unos métodos para inicializar las habilidades y otros para hacer uso de ellas; como setHabilidadArma(IHabilidadArma implementacion) o UsarHabilidadArma().

public abstract class PersonajeAbstracto

{

    #region POSIBLES HABILIDADES DE LA CRIATURA

 

    private IHabilidadArma HabilidadDeArmas;

    private IHabilidadMagia HabilidadMagica;

    private IHabilidadVolar HabilidadDeVolar;

       

    #endregion

 

    #region ELEMENTOS COMUNES A TODOS

 

    public abstract string DisplayName { get; }

    public int PuntosDeVida { get; set; }

    public void Desplazarse() { /* Implementa desplazamiento */}

       

    #endregion

 

 

    #region INICIALIZADORES DE COMPORTAMIENTOS

 

    // Con estos métodos podemos cambiar los comportamientos en tiempo de

       ejecución

       

    public void setHabilidadArma(IHabilidadArma habArma)

    {

        this.HabilidadDeArmas = habArma;

    }

    public void setHabilidadMagia(IHabilidadMagia habMagia)

    {

        this.HabilidadMagica = habMagia;

    }

    public void setHabilidadVolar(IHabilidadVolar habVolar)

    {

        this.HabilidadDeVolar = habVolar;

    }

       

    #endregion

 

 

    #region EJECUTAR HABILIDADES

 

    public void UsarHabilidadArma()

    {

        this.HabilidadDeArmas.UsarArma();

    }

 

    public void UsarHabilidadMagia()

    {

        this.HabilidadMagica.UsarMagia();

    }

 

    public void UsarHabilidadVolar()

    {

        this.HabilidadDeVolar.Volar();

    }

 

    #endregion

       

       

 

}

 

Cada implementación de esta clase abstracta en su constructor inicializa las habilidades correspondientes a cada criatura.

public class Elfo : PersonajeAbstracto

{

 

    // constructor

    public Elfo()

    {

        // un elfo usa su arco

        this.setHabilidadArma(new HabilidadArmaArco());

        // un elfo no sabe usar magia

        this.setHabilidadMagia(new NoHabilidadMagia());

        // un elfo vuela :P

        this.setHabilidadVolar(new HabilidadVolar());

    }

 

    public override string DisplayName { get { return "Elfo"; }

 

    }

}

 

En este caso hemos creado un elfo que dispara flechas con su arco, no tiene capacidad de usar magia y aparte de esto, nuestro elfo vuela, entre otras cosas por que a mí me da la gana. :P

 

Como los métodos que asignan las habilidades son públicos, podríamos hacer que cambiaran estas habilidades en tiempo de ejecución. Por ejemplo si queremos que nuestro elfo no vuele, podemos usar el código marcado en amarillo:

 

// ELFO

PersonajeAbstracto elfo = new Elfo();

Console.WriteLine(elfo.DisplayName);

// usa tu habilidad para disparar con arco

elfo.UsarHabilidadArma();

// intenta usar magia

elfo.UsarHabilidadMagia();

// los elfos vuelan ¿?

elfo.UsarHabilidadVolar();

// un poderoso mago hechiza a nuestro elfo y le deshabilita la capacidad de volar!!!

elfo.setHabilidadVolar(new NoHabilidadVolar());

// Noooooooooo!!! ya no puedes volar!!!

elfo.UsarHabilidadVolar();

 

Command window 

 

 

Podemos ver un diagrama genérico de nuestras criaturas:

Diagrama Estrategia

 

El código fuente lo podéis descargar en el siguiente enlace:
http://blogs.renacimiento.com/cmerino/Source/PatronEstrategia.rar

 

June 28
Desacoplar implementaciones con Plugins en C#

Introducción

Los plugins, como todos sabemos, sirven para aumentar o modificar funcionalidad dentro de una aplicación que ya existe.  Su instalación es sencilla y no implica recompilar  la aplicación.

Podemos usar el concepto de plugin para conseguir desacoplar las diferentes implementaciones de una interfaz, consiguiendo que el código que consume esas implementaciones solamente necesite conocer las interfaces, sin importarle los entresijos de las implementaciones.

La idea es la siguiente; un objeto cliente consume un objeto que cumple con cierta interfaz. Cada una de las implementaciones de esa interfaz está en un ensamblado diferente, es decir, cada implementación es una dll (plugin) que podemos instalar o desinstalar para cambiar o añadir funcionalidad.

Conceptos preliminares

Acoplamiento:
El acoplamiento es el grado de dependencia entre clases. La forma más sencilla para conocer el acoplamiento en nuestra aplicación es intentar instanciar una de nuestras clases de forma aislada.
El acoplamiento impide la reutilización del código por ser demasiado concreto. Es fácil reusar  módulos de bajo nivel en forma de bibliotecas de rutinas. Cuando los módulos de alto nivel dependen de los módulos de bajo nivel, es muy difícil reutilizarlos en diferentes contextos. Sin embargo, cuando los módulos de alto nivel son independientes de los de más bajo nivel, es mucho más sencillo reutilizarlos.

Por ejemplo, un programa que obtiene información introducida por teclado y la copia a una impresora. Un diseño concreto sería el siguiente:
Diseño acoplado
Los módulos que leen del teclado y escriben en la impresora podría ser reutilizado para otras aplicaciones fácilmente, sin embargo Copy sería más difícil puesto que tiene la restricción que copia desde el teclado a una impresora.

Sin embargo, si desacoplamos las clases podemos obtener un diseño similar al siguiente:

Diseño desacoplado
Sería mucho más sencillo reutilizar la función Copy, puesto que solo conoce acerca de un origen (Reader) y de un destino (Writer) haciéndolo mucho más flexible.

Inyección de dependencias:
Una de los motivos de que se produzca acoplamiento es cuando nuestros objetos de negocio crean otros objetos que necesitan utilizar. Esta tarea de creación de objetos se lo podemos dejar a un módulo exclusivo para estas tareas a modo de factoría de objetos.  Esta técnica se denomina Inversión de Control debido a que en vez de nosotros tener el control sobre la creación de objetos, le pedimos a otro que lo haga.
La inyección de dependencias es un tipo de Inversión de Control. Funciona de una forma similiar a una factoría de objetos y aparte de esto se encarga de resolver las dependencias desacoplando las interfaces de las implementaciones.

Caso de estudio: el diccionario multiidioma.

Se trata de una aplicación que va a mostrar el contenido de diccionarios de diferentes idomas.
Podremos seleccionar de un listado un idioma para poder hacer búsquedas en el diccionario correspondiente.

Cada diccionario es una dll colocada en cierto directorio, que funcionará a modo de plugin. Según vayamos añadiendo plugins iremos teniendo disponibles más diccionarios. Cada plugin implementa la lógica de cada diccionario especificando en su código la forma de obtener la información y las entradas que va a devolver. Todos los plugins implementan una interfaz común que será lo único que entiende la aplicación: IDiccionario.

Un esquema sencillo de la aplicación es el siguiente:

Diagrama Plugin

 

La presentación solo va a conocer la clase DiccionarioBFLL, que hace de fachada de negocio de todas las operaciones que queremos hacer con los diccionarios, y la Interfaz IDiccionario que especifica las acciones que podemos realizar con los diccionarios instalados.

DiccionarioBFLL usará un objeto de tipo Inyector, el cual va a obtener las instancias de los diferentes tipos de diccionarios instalados como plugins.

public static List<IDiccionario> GetPlugIns(List<Assembly> assemblies)

{

  List<Type> availableTypes = new List<Type>();

 

  foreach (Assembly currentAssembly in assemblies)

                availableTypes.AddRange(currentAssembly.GetTypes());

 

  return availableTypes.ConvertAll<IDiccionario>(delegate(Type t)

   {

     return Activator.CreateInstance(t) as IDiccionario; <- Inyección

   });

}

 

Como parámetro usamos una lista de ensamblados obtenidos con la clase PluginLoader, que va a leer los ensamblados (plugins) situados en cierto directorio.

public static List<Assembly> LoadPlugInAssemblies()

{

  DirectoryInfo dInfo = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "Plugins"));

 

  if (!dInfo.Exists)

  {

    dInfo = new DirectoryInfo(ConfigurationManager.AppSettings["RutaPlugins"]);

  }

 

  FileInfo[] files = dInfo.GetFiles("Dic*.dll");

  List<Assembly> plugInAssemblyList = new List<Assembly>();

 

  if (null != files)

  {

    foreach (FileInfo file in files)

    {

      plugInAssemblyList.Add(Assembly.LoadFile(file.FullName));

    }

  }

 

  return plugInAssemblyList;

}

 

El resto del código se encuentra en el código fuente de esta sencilla solución.

http://blogs.renacimiento.com/cmerino/Source/MultiDiccionario.rar

Hay muchas maneras de implementar la inyección de dependencias, esta es una de tantas. Existen frameworks que facilitan el uso de esta técnica como Unity (Incluído en la Enterprise Library) o Spring.Net.

 

Referencias:
http://martinfowler.com/articles/injection.html
http://www.objectmentor.com/resources/articles/dip.pdf
http://msdn.microsoft.com/en-us/library/ff650320.aspx

 

 

 About this blog

 
About this blog
Welcome to SharePoint Blogs. Use this space to provide a brief message about this blog or blog authors. To edit this content, select "Edit Page" from the "Site Actions" menu.