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

viernes, 13 de septiembre de 2013

Gestión de versiones en el GAC

Cuando se desarrolla una librería para ser distribuida públicamente es normal que esta pueda tener actualizaciones de versiones. Bien porque corrige algunos fallos o porque introduce nueva funcionalidad. Sobre el sistema de versionados ya hablé en un post anterior.

En esta ocasión me quiero centrar en las actualizaciones automáticas de estos ensamblados de las librerías en el GAC. Me explico con un ejemplo. Supongamos que hemos publicado una librería (v1.0.0.0) para que pueda ser usada por múltiples aplicaciones y componentes. Sacamos una nueva versión que corrige algunos errores (v1.0.0.1) y que mantiene la compatibilidad con la versión anterior.

Al ir firmadas con un Strong Name, las aplicaciones ignoran la nueva versión por completo, obligando a que estas deban ser recompiladas haciendo referencia a la nueva versión. Estamos dejando en manos de los desarrolladores que usan nuestras librerías la tarea de actualizar sus aplicaciones a la nueva versión. Si el número de aplicaciones es grande o el problema solucionado es grave (supongamos un agujero de seguridad), el escenario no es muy halagüeño.

Lo ideal es que las aplicaciones se adapten automáticamente a las nuevas versiones de ensamblados sin que a priori se necesite la re-compilación de las aplicaciones. Y digo a priori porque deben ser los desarrolladores de las aplicaciones los que establezcan si realmente quieren este comportamiento o no.

Redirección de versiones de ensamblado

.Net nos permite este tipo de políticas a través de su redirección de ensamblados. Esto es, podemos indicar a una aplicación que use versiones más modernas de un ensamblado.

Este tipo de políticas se pueden realizar en 3 niveles:

  • A nivel de aplicación. Cada aplicación decide su política de actualizaciones. Es decir, que versión van a usar en caso de que haya varias versiones del mismo ensamblado, o si realmente no quieren que esta política se lleve a cabo.
  • A nivel de máquina. Este es poco frecuente de usar. Normalmente el administrador de la máquina decide si se debe usar una versión concreta o no de un ensamblado en las aplicaciones instaladas que la usan.
  • A nivel de proveedor. Los proveedores de las librerías definen en el GAC la política de versiones a usar. De manera que cuando deciden que dos versiones son compatibles, las aplicaciones usarán automáticamente la versión más moderna. Aun así, se puede indicar que una aplicación no siga esta política y que continúe usando la versión de ensamblado con la que fue creado.

En los tres niveles, el esquema para definir la política de redirección se realiza de la misma manera a través de XML:

image

PublicherPolicyFile.xml

  • <assemblyBinding> define el bloque de redirección y sólo puede haber uno por fichero de configuración.
  • <dependantAssembly> puedes tener tantos bloques como necesites aunque cada uno sólo debe contener un elemento <assemblyIdentity>.
  • <assemblyIdentity> define el nombre del ensamblado a redireccionar y su publicKeyToken para poder ser identificado sin que haya ninguna ambigüedad.   
  • <bindingRedirect> define la redirección de la versión. en la parte de oldVersion soporta tanto una versión en concreto (como es el caso del ejemplo) como un rango de versiones.

    No es necesario indicar de manera individual cada una de las versiones que queremos redirigir. También podemos indicar rangos de versiones y comodines:

  • image

    Si queremos usar la política de actualizaciones a nivel de aplicación, basta con poner la configuración en el fichero de configuración de la aplicación tal y como se muestra en el código XML anterior.

    En caso de ser nosotros los que distribuyamos las librerías, necesitaremos crear un fichero de políticas de versionado a nivel de proveedor. A continuación se explica cómo hacerlo.

    Crear un fichero de políticas de proveedor

    La creación de este fichero de políticas pasa por 3 sencillos pasos:

    1. Crear el fichero XML con la configuración de la política. Básicamente como se hizo en el ejemplo anterior.
    2. Crear el ensamblado con la configuración de la política.
    3. Añadir el ensamblado con la política de redirección al GAC.

    El paso 1 es básicamente crear el fichero tal y como se define en el XML del ejemplo, donde para el ensamblado ArriviSoft.RedirectingAssembllies.Library se ha definido que todos aquellas aplicaciones que referencien a la versión 1.0.0.0, pueden utilizar la versión 1.0.0.1

    Para crear el ensamblado que contendrá la política de redirección debemos usar la herramienta Assembly Linker (al.exe) que viene con Visual Studio.

    Ejecutamos el siguiente comando:

    al.exe /link:PublisherPolicyFile.xml  /out:Policy.1.0.ArriviSoft.RedirectingAssemblies.MyLibrary.dll  /keyfile:ArriviSoft.snk  /platform:x86

    donde:

    • /link: Indica el fichero xml con la política de redirección de ensamblados.
    • /out: Indica el ensamblado que se va a generar y que contendrá la política de redirección. Atención con el formato de nombre de fichero pues debe ser:
      "Policy.majorVersion.minorVersion.NombreEnsamblado.dll"
    • /keyfile: Fichero con el par de claves que además debe ser el mismo con el que se firme el ensamblado.
    • /platform: Permite definir la plataforma de compilación (x86, amd64, ia64, o MSIL) para afinar mejor.
      Desgraciadamente no podremos generar el mismo ensamblado de políticas si definimos distintas plataformas dado que el nombre del ensamblado resultante sería el mismo. Para solucionarlo bastaría con indicar la plataforma dentro del propio nombre de ensamblado.

    Finalmente, una vez tengamos el ensamblado con la política de redirección, este ya puede ser usado en los equipos clientes. El instalador de nuestra librerías debe registrar en el GAC la nueva versión de ensamblados junto con el ensamblado de políticas usando el comando Global Assembly Cache tool (Gacutil.exe)

    miércoles, 6 de febrero de 2013

    Enseñando licenciamiento de software con .NET a mi abuela

    Mi abuela es una señora muy adelantada a su tiempo y con muchas inquietudes sobre las nuevas tecnologías. No hace mucho le dio por empezar a programar en C# y a veces las dudillas que le surgen me las pregunta.

    El otro día tuvimos una interesante conversación sobre el licenciamiento y me he decidido a intentar transcribir nuestra conversación en una entrada. Puede ser un poco larga pero de seguro que merece la pena.

    - Pues mira nieto, el otro día hice una aplicación para gestionar residencias de ancianos. Parece que tiene buena acogida y que gusta, pero con el esfuerzo que me ha supuesto, no quisiera que se pusieran a copiársela entre las residencias. Que esos son muy listos para aprovecharse de los pobres ancianos. ¿Cómo puedo hacer para evitar que se lo copien mi aplicacioncita de unos a otros?

    - Claro que si abuela. Me imagino lo mal que se tiene que sentir uno al encontrarse sus cosas en el emule, torrents o RapidShares de turno…

    - Niño, ¿de que me hablas? Yo me refería que se lo mandan por mail o los copian en CDs…

    - Claro que si abuela. De hecho .NET tiene modelos que te permiten proteger, o mejor dicho, licenciar tus aplicaciones de manera muy sencilla y flexible. Si quieres te explico cómo se hace.

    - Claro que si nieto. Pero explícamelo sencillito que tu ya sabes que a mi me cuesta un poco con estas edades…

    - Bueno, bueno. A ver abuela. ¿Sabes de lo que hablo si digo atributos de clase?

    - Pues no estoy del todo segura. Anda, recuérdamelo. Y mejor si es con un ejemplillo.

    - Bueno abuela. Los atributos de clase son unos modificadores que se colocan justo antes de definir las clases. Y se colocan entre corchetes. Hay muchos atributos de clase, de hecho, puedes usar varios para una misma clase. Por ejemplo, este atributo sirve para indicar que una clase es serializable.

       1:  [Serializable]
       2:  class Foo
       3:  {
       4:     //...
       5:  }



    Como ya te he comentado, abuela, hay muchos más. Pero el que a nosotros nos interesa para el licenciamiento se llama System.ComponentModel.LicenseProvider.


    - O sea, que las protecciones se hacen usando ese atributo de clase, ¿no?


    - Exactamente abuela. Cada vez que usemos este atributo en una clase, significará que esta estará protegida por una licencia.


    - Vale. Entonces la clase que yo quiera proteger debe quedar más o menos así, ¿no?


       1:  [System.ComponentModel.LicenseProvider]
       2:  public class Foo
       3:  {
       4:     // ...
       5:  }



    Bien, nietecito. Hasta aquí lo entiendo. Aunque me temo que lo que sigue a partir de ahora será más complicado, ¿verdad?


    - Que va abuela. Mira. Verás que sencillo. Cuando una clase viene con ese atributo, significa que va a recibir un objeto licencia. Esta licencia es una instancia de la clase System.ComponentModel.License y es muy sencillita:


       1:  namespace System.ComponentModel
       2:  {
       3:      public abstract class License : IDisposable
       4:      {
       5:          protected License();
       6:          public abstract string LicenseKey { get; }
       7:          public abstract void Dispose();
       8:      }
       9:  }



    Lo interesante de esta clase es que es abstracta, que implementa la interfaz IDisposable y su propiedad LicenseKey es de sólo lectura.


    - Ya veo. Entonces voy a recibir una instancia de "algo" que hereda de System.ComponentModel.License. Porque como dices, es una clase abstracta. Lo que no me queda claro es quién crea esa instancia ni cómo lo hace.


    - Ahora vamos a eso, abuela. El sistema de licenciamiento que tiene .NET funciona de la siguiente manera: La clase que quieres proteger va a recibir un objeto que hereda de License. Este objeto License es generado por una entidad proveedora de licencias a través del gestor de licencias que tiene el framework de .NET.


    Explicado de otra manera. Cuando la clase que quieres licenciar se esta creando, le pide al gestor de licencias (System.ComponentModel.LicenseManager) una licencia. Este entregará dicha licencia haciendo uso del generador o proveedor de licencias (System.ComponentModel.LicenseProvider)


    - O sea,que LicenseManager va a retornar la licencia que a su vez retorne LicenseProvider a la clase a proteger. Aquí hay gato encerrado o hay algo que todavía no me has contado. La clase License es abstracta, asi que me falta una clase License que en cualquier caso, parece que siempre la devuelve LicenseProvider. Con lo cual, no veo que hay de bueno en todo esto.


    - Claro abuela, te falta añadirle toda la lógica del sistema de licenciamiento. El framework de .NET te da la base y tu la completas.


    - Ay madre, que ya me asustas. ¿Cómo es eso de que yo tengo que poner la lógica a todo esto?


    - Que no es para tanto, abuela. Lo que hay que hacer es crearse una clase que permita devolver las licencias que queramos usando la lógica que necesitemos. Y por supuesto, la licencia que esta devuelva podrá ser como queramos siempre y cuando herede de System.ComponentModel.License.


    La clase generadora de licencias se le pasará como argumento al atributo de clase System.ComponentModel.LicenseProvider. De manera que el proveedor pueda invocar a nuestra clase generadora para obtener la licencia correspondiente.


    - Oye. ¿Y es muy difícil construir una clase generadora de licencias de estas? Me vas a tener que enseñar un ejemplo de esto que me estás contando, ¿eh?


    - Bueno, tan difícil como queramos hacerlo nosotros. Microsoft de hecho nos dejó una creada muy sencillita. De hecho es tan sencillita que se aconseja no usarla para nuestros desarrollos. Esta clase es System.LicenseProvider.LicFileLicenseProvider.


    Te enseño un ejemplo de cómo se usaría este generador:


       1:  [System.ComponentModel.LicenseProvider(typeof(System.ComponentModel.LicFileLicenseProvider))]
       2:      public class Foo
       3:      {
       4:          private System.ComponentModel.License _license = null;
       5:   
       6:          public Foo()
       7:          {
       8:              _license = System.ComponentModel.LicenseManager.Validate(this.GetType(), this);
       9:          }
      10:      }



    Concretamente LicFileLicenseProvider funciona de la siguiente manera:


    1. En la carpeta donde se encuentra el ensamblado con la clase a proteger, busca un fichero con extensión .lic y nombre igual que el nombre del namespace con la clase a proteger, un punto y el nombre de la clase a proteger.


    2. El fichero .lic debe tener el siguiente contenido de texto: "Namespace+clase is a licensed component.", donde Namespace + clase es el nombre del namespace de la clase que queremos proteger.


    - Entiendo, entiendo. Pero no veo por qué no quieres que se use ese tipo de proveedor de licencias.


    - ¡Abuela! Cualquiera que supiese que estas usando ese licenciamiento puede crearse manualmente el fichero .lic y… adiós protección.


    - Bueno, bueno. Por cierto, ¿qué pasa si el torpe de turno va y borra o modifica el contenido de ese fichero?


    - En ese caso, la llamada en el constructor de tu clase a Validate() fallará y lanzará una excepción de tipo System.ComponentModel.LicenseException. Si no la capturas, el licenciamiento habrá funcionado, puesto que no se ha creado ninguna instancia de la clase protegida, pero posiblemente el mensaje de error que lance la aplicación no será muy descriptivo para el usuario.


    Las excepciones, cuando se producen, suelen ser bastante lentas, así que si únicamente quieres saber si existe licencia, puedes usar el método IsValid, en lugar de Validate. Este te retornará un booleano indicando si la licencia es valida o no. Pero no te devolverá ninguna licencia.


    - Entonces, realmente no necesito ninguna clase License. Me podría bastar con llamar a IsVaild y así saber si se puede ejecutar o no la aplicación, ¿no?


    - Bueno, abuela, de eso se trata. El modelo es así de flexible. Si no lo necesitas, no lo uses. Pero fíjate lo que se puede llegar a montar. Imagínate que a pesar de no tener una licencia buena, quisieras permitir que la aplicación se ejecutara. Por ejemplo, como una versión de prueba. O que dependiendo de la licencia, se pudieran ejecutar unas u otras opciones de la aplicación.


    - ¡Pues claro! Así si que sería fácil, por ejemplo, vender por separado el modulo de gestión de enfermeros y por otro lado la gestión de las medicinas… ¡Realmente interesante!


    - Exacto abuela. Y en la propiedad License.LicenseKey podrías poner el identificador del cliente.


    Resumiendo. Tenemos 4 actores en esto de la gestión de licencias: La clase que queremos licenciar, el tipo de licencia, el proveedor de licencias y el gestor de licencias.


    - Si, lo voy entendiendo. Pero, ¿cómo podría crearme mi propio proveedor de licencias?


    - No es difícil, abuela. Basta con crearse una clase que herede de LicenseProvider y otra que herede de License:


       1:  using System.ComponentModel;
       2:   
       3:  /// My License Provider class
       4:  class MyLicenseProvider : LicenseProvider
       5:  {
       6:      public override License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
       7:      {
       8:          return new MyLicense();
       9:      }
      10:  }
      11:   
      12:  // My License class
      13:  class MyLicense : License
      14:  {
      15:      public override string LicenseKey
      16:      {
      17:          get { return "Some license key"; }
      18:      }
      19:  }



    Este es un ejemplo muy sencillo. Realmente no estamos haciendo ninguna comprobación de nada. Siempre devuelve una licencia buena.


    - Ya creo que me voy enterando. De todas maneras no estoy muy segura de la propiedad LicenseKey de la licencia. Si es de sólo lectura y toda la lógica para la generación de la licencia reside en el proveedor. Entonces le tendré que pasar el valor del LicenseKey a través del constructor, ¿no? Sino, no lo veo yo. Luego dependiendo de ese LicenseKey la aplicación permitiría o no hacer determinadas acciones. ¿Cierto?


    Más o menos esta sería mi clase Licencia, que recibiría el License Key a través del constructor:


       1:  class MyLicense : License
       2:  {
       3:      private string _licenseKey = null;
       4:   
       5:      public MyLicense(string licenseKey)
       6:      {
       7:          _licenseKey = licenseKey;
       8:      }
       9:   
      10:      public override string LicenseKey
      11:      {
      12:          get { return _licenseKey; }
      13:      }
      14:   
      15:      public override void Dispose()
      16:      {
      17:      }
      18:  }



    Mi clase Proveedora de licencias que, a modo de ejemplo, devuelve una licencia siempre con la misma clave:


       1:  using System.ComponentModel;
       2:   
       3:  class MyLicenseProvider : LicenseProvider
       4:  {
       5:      public override License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
       6:      {
       7:          return new MyLicense("Full license");
       8:      }
       9:  }




    Y por último mi clase protegida. En este caso quiero proteger la llamada a DoSomething chequeando el valor de la clave de licencia:


       1:  using System.ComponentModel;
       2:   
       3:  [LicenseProvider(typeof(MyLicenseProvider))]
       4:  public class Foo
       5:  {
       6:      private License _license = null;
       7:   
       8:      public Foo()
       9:      {
      10:          _license = LicenseManager.Validate(this.GetType(), this);
      11:      }
      12:   
      13:      public void DoSomething()
      14:      {
      15:          if (_license.LicenseKey != "Full license")
      16:          {
      17:              throw new LicenseException(this.GetType(), this, 
      18:                             "You don´t have a valid license to invoke this method.");
      19:          }
      20:   
      21:          // Do something here ...
      22:      }
      23:  }



    - Bueno abuela, claro que lo podrías hacer así. Pero te estarías cargando toda la filosofía del modelo de licencias. Si en cualquier momento quisieras cambiar la forma de proteger tu aplicación, tendrías que modificar en todos los sitios de la clase protegida donde se comprueba el valor de la clave de licencia.


    Realmente a la aplicación, o en tu ejemplo, a la clase protegida, le da absolutamente igual el valor de LicenseKey. El hecho de que se haya podido llamar a la función ya significa que hay una licencia valida pues el constructor ya la ha validado.


    - Ah! Entonces también la lógica de saber si las claves de licencia son buenas o no, de si se puede ejecutar cierta función y demás, se hace también en el proveedor. ¿Cierto?


    - Exactamente. Piensa en tu licencia como un contrato o un permiso de utilización de la clase protegida. Si tiene licencia, funciona, sino, ni siquiera puede existir. Además, si fuera necesario, podría indicar determinadas limitaciones.


    - Comprendo, comprendo. Es como tu carnet de conducir, ¿no? Si tienes licencia, puedes llevar el coche, y si no, no. Pero además, tu carnet también te limita los vehículos que puedes conducir, ¿verdad?


    - Eso es abuela, muy buen ejemplo. Me lo tengo que apuntar para cuando se lo tenga que explicar a otras personas. Jejeje.


    - Entonces el ejemplo que he dicho antes no sirve. Mejor sería poner algo así:


    Mi licencia es capaz de indicar si se está en modo demostración. En este modo habrá operaciones que no se pueden ejecutar:


       1:  class MyLicense : License
       2:  {
       3:      private string _licenseKey = null;
       4:   
       5:      public MyLicense(string licenseKey, bool isDemoVersion)
       6:      {
       7:          _licenseKey = licenseKey;
       8:          this.IsDemoVersion = isDemoVersion;
       9:      }
      10:   
      11:      public override string LicenseKey
      12:      {
      13:          get { return _licenseKey; }
      14:      }
      15:   
      16:      public bool IsDemoVersion { get; private set; }
      17:   
      18:      public override void Dispose()
      19:      {
      20:      }
      21:  }



    Mi clase proveedora de licencias determina si la licencia a generar es de tipo demostración o no. En mi ejemplo, simplemente es de demostración a partir de 2013:


       1:  class MyLicenseProvider : LicenseProvider
       2:  {
       3:      public override MyLicense GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
       4:      {
       5:          // Demo version since 2013
       6:          if (DateTime.Now.CompareTo(new DateTime(2013, 1, 1)) > 0)
       7:          {
       8:              return new MyLicense("Full license", true);
       9:          }
      10:          else
      11:          {
      12:              return new MyLicense("Full license", false);
      13:          }
      14:      }
      15:  }



    Por último, la clase que estoy protegiendo ya no tiene que tener ninguna lógica respecto a la licencia. Sólo chequear que no está en modo demostración:


       1:  [LicenseProvider(typeof(MyLicenseProvider))]
       2:  public class Foo
       3:  {
       4:      private MyLicense _license = null;
       5:   
       6:      public Foo()
       7:      {
       8:          _license = LicenseManager.Validate(this.GetType(), this) as MyLicense;
       9:      }
      10:   
      11:      public void DoSomething()
      12:      {
      13:          // Demo version cannot execute this code!!!
      14:          if (_license.IsDemoVersion)
      15:          {
      16:              throw new LicenseException(this.GetType(), this, 
      17:                             "You don´t have a valid license to invoke this method.");
      18:          }
      19:   
      20:          // Do something here ...
      21:      }
      22:  }



    - Mucha mejor pinta, abuela. Tampoco podemos olvidar que la llamada a Validate nos ofrece otros argumentos la mar de interesantes. Vamos a verlos.



    • LicenseContext context. Este objeto nos da el contexto en el cual se va a usar la clase a proteger. Quizás su propiedad más importante sea UsageMode, el cual nos va a permitir saber si estamos ejecutando una aplicación normal de escritorio o bien si estamos usando la clase en modo diseño dentro de Visual Studio. Por ejemplo, para licenciar componentes o controles gráficos para otros desarrolladores.
    • Type type. Nos da el tipo de la clase a licenciar. Así podemos usar el mismo proveedor de licencias para proteger varias clases.
    • object instance. Si no fuese sufieciente saber el tipo, tambien tenemos a nuestra disposición la instancia de la clase a proteger.
    • bool allowExceptions. Nos indica si debemos lanzar una excepción LicenseException en caso de no cumplir con la licencia. Y esto tiene sentido pues acuerdate de la llamada IsValid que no lanza excepciones. Básicamente Validate e IsValid usan la llamada a esta función. Una permite excepciones y la otra no.

    Sin meternos en historias de dar soporte a distintos tipos de clases a proteger ni nada de eso, tu clase proveedora de licencias quedaría mejor así:


       1:  class MyLicenseProvider : LicenseProvider
       2:  {
       3:      public override MyLicense GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
       4:      {
       5:          // Full license until 2013
       6:          if (DateTime.Now.CompareTo(new DateTime(2013, 1, 1)) < 0)
       7:          {
       8:              return new MyLicense("Full license", false);
       9:          }
      10:          // Demo version between 2013 - 2014
      11:          else if (DateTime.Now.CompareTo(new DateTime(2014, 1, 1)) < 0)
      12:          {
      13:              return new MyLicense("Demo version", true);
      14:          }
      15:          // No license from 2014
      16:          else
      17:          {
      18:              if (allowExceptions)
      19:              {
      20:                  throw new LicenseException(type, instance, "Invalid license.");
      21:              }
      22:              else
      23:              {
      24:                  return null;
      25:              }
      26:          }
      27:      }



    Y así nuestra clase protegida puede usar IsValid en lugar de Validate sin preocuparse de si se van a producir excepciones.


    Es muy  importante que te quede claro que toda la lógica de la licencia debe quedar dentro de la clase de generadora de licencias. Esta puede hacer todo lo que se te ocurra. Desde leer un fichero de texto como hace LicFileLicenseProvider, hasta conectarse a un servidor remoto en otro continente para obtener la licencia.


    - Pues si nietecito, ya tengo más o menos claro todo esto de las licencias. Ya puedo estar segura de que nadie se va a copiar mi programa de residencias de ancianos.


    - Me alegro de haberte ayudado abuela. De todas maneras, siempre habrá alguien que encuentre la manera de saltarse la licencia. Pero todo dependerá de lo importante que sea lo que tengamos que proteger y el tiempo que le queramos dedicar poniendo trampas y demás dificultades a estos individuos. Recuerda, no hace falta crear la super licencia que protege los sistemas del Pentágono. La mayoría de las personas lo dará por imposible sólo al ver que ya tiene licencia. Y eso es lo importante.

    martes, 15 de febrero de 2011

    YAML. Un lenguaje estructurado pero no de marcado

    YAML, cuyas iniciales dicen YAML Ain't Markage Language, es un lenguaje que me ha sorprendido. Lo descubrí a raíz de un artículo en el que comentaban su uso en la introducción de estructuras de datos por personas que no estaban relacionadas con el desarrollo software.
    Y es que es cierto que estamos muy acostumbrados a usar lenguaje XML o derivados, del que .NET tiene muchas librerias, o incluso JSON para otras plataformas.
    Xml no está pensado realmente para ser leido por una persona. Aunque para ficheros pequeños y con no demasiadas estructuras anidadas, podamos apañarnos bien.

    YAML es un lenguaje estructurado, pero sin etiquetas. Lo cual lo hace extremadamente fácil de leer para una persona. Definido a partir de una serie de reglas muy símples que hace que escribir un documento con este formato sea realmente fácil.

    Las andaduras de este lenguaje comenzaron en Mayo de 2001 por Clark Evans. Y actualmente está en la versión de especificación 1.2. La página de referencia la podeis encontrar aquí. Y que precisamente tiene todo su contenido escrito con estructura YAML.

    Una pequeña lista de sus características son:

    • YAML tiene un formato de lectura sencillo para las personas.
    • YAML es conciso y compacto.
    • YAML es expresivo y extensible.
    • YAML no es un lenguaje de marcado!

    Si esta lista la quisiera expresar con XML necesitaría algo así:

    <ul>
       <li>YAML tiene un formato de lectura sencillo para las personas.</li>
       <li>YAML es conciso y compacto.</li>
       <li>YAML es expresivo y extensible.</li>
       <li>YAML no es un lenguaje de marcado!</li>
    <ul>

    En YAML se definiría de la siguiente manera:

    - YAML tiene un formato de lectura sencillo para las personas.
    - YAML es conciso y compacto.
    - YAML es expresivo y extensible.
    - YAML no es un lenguaje de marcado!

    Otros ejemplos de estructuras que pueden hacerse con YAML:

    Diccionarios:

    ---
    Clave 1: Valor de la clave 1.
    Clave 2: Valor de la clave 2.
    Clave 3: Valor de la clave 3.

    Listas de diccionarios anidados:

    ---
    - Clave 1: Valor de la clave 1.
    - Clave 2:
         Subclave 1: Valor de la Clave 2, Subclave 1.
         Subclave 2: Valor de la Clave 2, Subclave 2.

    Mapas:

    ---
    Seat Leon: {Color: Rojo, Carburante: Diesel, Velocidad Máxima: 185 Km/h, Kilometros: 25000}
    Nissan Micra: {Color: Blanco, Carburante: Hibrido, Velocidad Máxima: 172 Km/h, Kilometros: 12000}
     

    Existen implementaciones de este formato en muchos lenguajes, incluyendo .NET, el cual dispone de una implementación en Codeplex y que acompaña con un minitutorial de Yaml en 5 minutos.

    miércoles, 9 de febrero de 2011

    Versionado de ensamblados de .NET

    Uno de los temas que menos cuidado se le suele prestar en el desarrollo de software es el del versionado de componentes. Y es que gracias a las versiones, podemos saber exactamente cuando se compiló un ensamblado (e incluso saber la etiqueta de la versión de código fuente que la generó) y si hay otros ensamblados que son compatibles.

    En .NET, las versiones se establecen en el fichero AssemblyInfo.cs que se puede encontrar dentro de la carpeta de “Properties” del proyecto:

        1 using System.Reflection;

        2 using System.Runtime.InteropServices;

        3 

        4 // General Information about an assembly is controlled through the following

        5 // set of attributes. Change these attribute values to modify the information

        6 // associated with an assembly.

        7 [assembly: AssemblyTitle("Project Title")]

        8 [assembly: AssemblyDescription("Some description")]

        9 [assembly: AssemblyConfiguration("Debug")]

       10 [assembly: AssemblyCompany("My Company of Software")]

       11 [assembly: AssemblyProduct("Product Name")]

       12 [assembly: AssemblyCopyright("Copyright © My Company, 2011")]

       13 [assembly: AssemblyTrademark("")]

       14 [assembly: AssemblyCulture("")]

       15 

       16 // Setting ComVisible to false makes the types in this assembly not visible

       17 // to COM components.  If you need to access a type in this assembly from

       18 // COM, set the ComVisible attribute to true on that type.

       19 [assembly: ComVisible(false)]

       20 

       21 // The following GUID is for the ID of the typelib if this project is exposed to COM

       22 [assembly: Guid("f446dcb3-68fb-49d9-a057-fe50383ce4c3")]

       23 

       24 // Version information for an assembly consists of the following four values:

       25 //

       26 //      Major Version

       27 //      Minor Version

       28 //      Build Number

       29 //      Revision

       30 //

       31 // You can specify all the values or you can default the Build and Revision Numbers

       32 // by using the '*' as shown below:

       33 // [assembly: AssemblyVersion("1.0.*")]

       34 [assembly: AssemblyVersion("1.0.0.0")]

       35 [assembly: AssemblyFileVersion("1.0.0.0")]

    Las últimas líneas del fichero están relacionadas con la versión del ensamblado y con la versión del fichero.

    AssemblyFileVersion

    Su principal objetivo es el de identificar de manera uniequivoca a un ensamblado. A partir de los valores de la versión podemos saber la versión de código fuente que compiló el ensamblado. Tambien nos permite reconocer si se trata de una versión de “Service Pack” o de “Hotfix”.

    Las versiones de un fichero están basadas en un código de 4 números. El uso de cada uno de los valores depende de cada equipo de desarrollo. Personalmente, les doy el siguiente significado a los valores de versión:

    • Major Version: Versión principal de producto. No suele cambiar a lo largo de todo el ciclo de desarrollo salvo que el desarrollo requiera rehacer todo el código desde cero. Evidentemente no se mantiene ninguna compatibilidad con versiones anteriores.
    • Minor Version: Cambio importante en las interfaces del ensamblado que hace que se rompa la compatibilidad con versiones anteriores. Al igual que la versión principal, este valor se cambia de manera manual.
    • Build Number: Número calculado automáticamente por el proceso de compilación en orden creciente de manera que dos compilaciones seguidas no generen el mismo valor. Existen muchos esquemas de generación de este valor. Quizá el más conocido es el de basado en el número de días transcurridos desde el 1 de Enero de 2000.
    • Revision: Número tambien generado automáticamente por el sistema de compilación. Por ejemplo, si se generan varias versiones el mismo día, la versión de Build no cambia, por lo que está va incrementandose de uno en uno por cada compilación.

    AssemblyVersion

    Los ensamblados de .NET usan esta versión para enlazarse con otros ensamblados. Esto es, cuando por ejemplo el ensamblado A.dll referencia al ensablado B.dll con version “1.0.0.0”, si sustituimos este último ensamblado por la versión “1.0.0.1”, A.dll no será capaz de encontrarlo pues espera encontrar la versión con la que fue compilado. Por este motivo no se debe dejar a la ligera la opción de Visual Studio de autoincrementar la versión del ensamblado en cada compilación.

    El significado de los valores de la versión puede ser:

    • Major Version: Se actualiza de manera manual. Normalmente significa un cambio radical en la estructura del ensamblado. Por ejemplo porque se ha vuelto a reescribir gran parte del código. Normalmente implica rompe la compatibilidad con versiones anteriores.
    • Minor Version: Se han añadido nuevas funcionalidades al ensamblado. Y por tanto, puede suponer cambios en la interfaz pero tratando de mantener la compatibilidad con versiones anteriores. Su modificación es manual.
    • Build Number: Valor calculado automatiamente y que identifica el número de compilación realizado sobre el código fuente.
    • Revision: Normalmente identifican ligeros cambios sobre el código. Por ejemplo solución de hotfixes. Aquellos ensamblados donde únicamente varíe el número de revisión son totalmente intercambiables entre sí.

    AssemblyInformationalVersion

    Este es otro atributo que puede ser usado dentro de AssemblyInfo. Su objetivo es el de exponer una versión del ensamblado a nivel de producto, documentación, instalador, etc. En ningún momento esta información es usada por Windows o por otros ensamblados de .NET.

    Esta versión se identifica con tres valores:

    • Major Release: Normalmente asociada a la versión principal de producto y no suele variar en todo el ciclo de desarrollo del proyecto salvo que se tenga que rehacer todo el contenido del ensamblado.
    • Minor Release: Este número se asocia a incrementos en la funcionalidad del ensamblado.
    • Maintenance Release: Los incrementos en este valor suelen reflejar pequeños cambios normalmente asociados a correción de errores, rendimiento o estabilidad. Los clientes del ensamblado no aprecian un cambio en la funcionalidad importada.

    domingo, 3 de octubre de 2010

    El patrón Modelo-Vista-Presentador (MVP) a examen

    Desde hace tiempo llevo enfrentándome con diversos patrones de diseño cuyo objetivo principal es el de separar la interfaz de usuario de la lógica de las aplicaciones. Desde mis años de estudiante universitario con Smalltalk y el patrón Modelo-Vista-Controlador (MVC), hasta MVVM con WPF, pasando por MVP, MP y sus variaciones.

    Cada vez que he tenido que me embarcaba a usar uno de estos patrones de diseño me encontraba con la misma curva de aprendizaje. Un concepto bastante sencillo pero que ante ciertos escenarios que encontraba en proyectos reales uno no sabe exactamente qué solución tomar.

    En esta entrada quiero centrarme en el patrón de diseño Modelo-Vista-Presentador (MVP), pero yendo más allá de la descripción académica de este patrón y enfocándome en cómo puede ser aplicado en aplicaciones reales.

    Como he comentado antes, MVP es otro patrón de diseño que tiene como objetivo separar la interfaz de usuario de la lógica de las aplicaciones.

    Básicamente este patrón consiste en 3 componentes:

    • La vista. Compuesta de las ventanas y controles que forman la interfaz de usuario de la aplicación.
    • El modelo. Que es donde se lleva a cabo toda la lógica de negocio.
    • El presentador. Escucha los eventos que se producen en la vista y ejecuta las acciones necesarias a través del modelo. Además puede tener acceso a las vistas a través de las interfaces que la vista debe implementar.

    image

    El concepto de este patrón es bastante sencillo. Por un lado tengo la vista, que se encarga de mostrar la información al usuario y de interactuar con él para hacer ciertas operaciones. Por otro lado, tenemos el modelo que, ignorante de cómo la información es mostrada al usuario, realiza toda la lógica de las aplicaciones usando las entidades del dominio. Y por último tenemos al presentador que es el que “presenta” a ambos actores sin que haya ningún tipo de dependencia entre ellos.

    El propósito del presentador tal y como se ha definido en este patrón no establece de manera clara el grado de control que este puede hacer de la vista.

    Dependiendo de este grado de control podemos encontrarnos variaciones en la manera de ver MVP. De hecho, algunas de ellas, dejan de ser MVP para convertirse en MVC, por lo cual debemos tener cuidado.

    MVP como Controlador Supervisado

    Por un lado, podemos tener un presentador que no gestione la forma en que la información es mostrada en la vista. Es la vista quien define la lógica de cómo la información es formateada y mostrada en la pantalla a partir de los controles que contiene. En este caso, el presentador únicamente los casos más complejos para facilitar el trabajo de la vista. Martin Fowler llama a esta variación Controlador Supervisado.

    image

    Cuando la vista recibe algún evento de ratón o teclado por parte del usuario, delega el control del evento en el presentador. Este puede realizar ciertas operaciones relacionadas con la vista como el control del estado de los controles y después realizar la llamada a algún comando en el modelo que realice la operación requerida por el usuario. El modelo realiza las operaciones pudiendo realizar cambios en su estado generando el evento correspondiente, el cual es manejado por la vista para actualizar los controles de la pantalla.

    Hay que tener en cuenta de que el hecho de que la vista pueda hacer referencia al modelo nos da como resultado un diagrama muy parecido al de MVC. Aunque este enfoque nos permita el uso de técnicas de Data Binding. Con lo cual la cantidad de código que la vista y presentador necesitan, se disminuye.

    Este enfoque nos va a permitir el uso de técnicas de Data Binding sobre el modelo. Por tanto, la cantidad de líneas de código fuente en la vista y el presentador disminuyen. Si nos fijamos en el diagrama, este es similar al de MVC por lo que hay que tener mucho cuidado en no caer en un cambio de patrón.

    MVP como Vista Pasiva

    Por otro lado, podemos hacer que el presentador gestione totalmente cómo la información se muestra en la vista. Es decir, tenemos una vista "tonta", sin ningún tipo de lógica, cuya única función es la de mostrar la información que se le pasa a través de la interfaz de la vista. Martin Fowler llama a esta variación Vista Pasiva.

    image

    En este caso, cuando un usuario realiza alguna operación sobre la interfaz de usuario, la vista delega los eventos sobre el presentador. Este realizará algún cambio sobre la vista para indicar el cambio de estado y hará las llamadas a los comandos sobre el modelo para llevar a cabo la operación requerida por el usuario. Cuando el modelo provoque cambios en su estado, estos serán recogidos por el presentador (al contrario que en el controlador supervisado, que era la vista quien atendía a estos cambios de estado en el modelo), el cual pedirá al modelo los cambios realizados para luego actualizar la vista acorde a los cambios recibidos.

    Ejemplo

    Supongamos el siguiente ejemplo: Un programa que para una fecha que le introduzcamos, te diga si es pasado, futuro o la fecha del día actual.

    image

    Cuando el usuario introduce la fecha y pulsa el botón OK, la vista delega el evento de pulsación de botón sobre el presentador. El presentador toma la fecha introducida del cuadro de texto y valida que el contenido tiene el formato de fecha correcto.

    Nota: He obviado la posibilidad de usar un control específico para introducir fechas y así ver en qué lugar se pude hacer la validación de datos.

    Si la fecha introducida no fuera correcta, el presentador comunicaría al usuario este hecho a través de la vista.

    En caso de que la fecha fuese correcta, el presentador realizaría la operación de comprobar la fecha usando el modelo. Al recibir el resultado de la operación, el presentador mostraría el resultado a través de la vista. Por ejemplo, modificando el texto de la etiqueta con el resultado.

    En el siguiente diagrama vemos la secuencia de llamadas realizada para el caso de Vista Pasiva:

    image

    En caso de usar la variación de Controlador Supervisado, supondría que la vista toma el resultado directamente del modelo y que además, la vista define la lógica de cómo el resultado se muestra en la pantalla. En el caso de que la validación de la fecha fallara, el presentador podría, por ejemplo, enviar una excepción hacia la vista que al capturarla decidiría como se mostraría al usuario.

    Actualización: 
    He creado en Codeplex un proyecto que hace uso de este patrón. Se trata de un sencillo ejemplo de máquina tragaperras sobre Windows Forms. El enlace al proyecto es este.

    domingo, 11 de julio de 2010

    Open eBook Reader

    Me acabo de estrenar en CodePlex subiendo un proyecto de mi propia cosecha. Se trata de un lector de libros electrónicos para PC. Lo he llamado Open eBook Reader y lo podéis encontrar en este enlace.
    Open eBook Reader
    La verdad es que lo hubiera llamado eBook Reader a secas, pero ya estaba pillado. En fin, que le he acoplado lo del Open delante porque lo he puesto con licencia de Open Source y porque pretendo que únicamente lea formatos de libros que también sean Open Source.
    Por ahora sólo lee formato Fiction Book v2.0. Ya iremos ampliando el repertorio de formatos.

    viernes, 4 de junio de 2010

    S.O.L.I.D. – El principio de la Responsabilidad Única (SRP) (parte 2)

    El Principio de Responsabilidad Única (o en inglés Single Responsibility Principle (SRP)) fue descrito por Tom DeMarco y Meilir Page-Jones en un trabajo que llamado “Cohesión” en el que se definen las relaciones de los elementos de un módulo. Este principio nos viene a decir que una clase sólo debería tener una única razón para cambiar.

    “Una clase debe tener una única razón para cambiar.”

    Lo que trata de decirnos este principio es que debemos huir de aquellas clases monolíticas que aglutinen varias responsabilidades. Pero, ¿qué es una responsabilidad? Desde el punto de vista de SRP se podría decir que una responsabilidad en una clase es una razón para cambiar esa clase. Es decir, si encontramos que hay más de una razón por la que una clase pueda cambiar entonces es que esa clase tiene más de una responsabilidad.

    ¿Y no sería más sencillo decir que una clase debería tener una sola razón para existir en lugar de para cambiar? Cuidado, porque esto nos podría llevar a hacer muy malos diseños de sistemas. Llevado al pie de la letra podría encontrarme con cientos de clases en mi sistema, cada una con una única función. Lo que haría al sistema nada mantenible.

    El punto clave que nos dice las razones por la que una clase puede cambiar va a depender del contexto en el que se va a dar uso a esa clase. Pongamos por ejemplo una clase que represente al motor de un coche. ¿Necesitamos conocer el régimen de revoluciones del motor?, ¿el peso?, ¿número de cilindros?, ¿presión del inyector de gasolina?, ¿o lo que nos interesa es simplemente poder arrancarlo y esperar que haga andar a un coche para llevaros de un sitio a otro? La respuesta a estas preguntas va a depender del contexto en el cual usemos la clase motor. No va a tener las mismas necesidades sobre esta clase un fabricante de coches que un usuario que usa el coche para ir de un sitio a otro. El fabricante de coches va a notar un número mayor de responsabilidades en el motor que el usuario del coche. Por tanto, para el fabricante, este principio recomendaría dividir la clase motor en otras más pequeñas que cumplan con las especificaciones de manera individual.

    Veamos otro ejemplo típico de violación del SRP: Supongamos que tenemos la clase Employee (Empleado) en un sistema de gestión de una empresa cualquiera. Esta clase nos permite realizar las tareas esperadas sobre un empleado: Cargor y almacenarlo en una base de datos, generar la nómina, información básica del empleado, etc.

    image

    Ahora supongamos dos aplicaciones que hacen uso de la clase Employee. Una para ser usada por el departamento de recursos humanos para la gestión de las nominas del personal y otra para la gestión de los proyectos que lleva la empresa.

    ¿Podemos pensar que no se está siguiendo el SRP? Lo que sería lo mismo, ¿creemos que la clase Employee tiene más de una razón por la que pueda cambiar? A mi se me ocurren unas cuantas: Cambiar el formato de almacenamiento de base de datos, modificar los campos que definen a un empleado, cambiar la lógica de generación de nóminas, etc. Es decir, esta clase tiene varias responsabilidades: Es responsable de la persistencia de los clientes, responsable de caracterizar a un empleado, responsable de generar las nóminas, etc.

    Las consecuencias de violar el SRP en este caso son dos:

    • La clase Employee tiene una dependencia con la clase AccountService para poder realizar el cálculo de las nóminas. Por tanto la aplicación de gestión de proyectos, a la hora de hacer el despliegue de esa aplicación, tambien debe incluir la librería que contiene esa clase aunque no la necesite.
    • Si alguna de las aplicaciones necesita implementar nueva funcionalidad y requiere cambiar la definición de la clase Employee, este cambio arrastraría cambios en el resto de aplicaciones que requerirían adaptarse a los nuevos cambios de la clase. En caso de olvidarnos, las consecuencias serían impredecibles.

    Una mejora en el diseño sería separar las responsabilidades en clases distintas

    image

    Hemos creado dos nuevas clases: una para la gestión de nóminas y otra para el almacenamiento en la base de datos. Hay que fijarse en el detalle de que la clase Employee no depende de las nuevas clases EmployeeAccount y de EmployeeStorage, sino que la dependencia la tienen las aplicaciones.

    En definitiva. Este principio es uno de los más simples de SOLID, y sin embargo de los más difíciles de implementar correctamente. Aplicando SRP,podemos alcanzar niveles más bajos de acoplamiento y una cohesión más alta del sistema.

    sábado, 29 de mayo de 2010

    S.O.L.I.D. Principios de Diseños Orientados a Objeto (parte 1)

    En la entrada anterior hablaba de los beneficios de hacer desarrollo software con un nivel bajo de acoplamiento, alta cohesión y una fuerte encapsulación. Cuando nos encontramos con proyectos de pequeño tamaño, estos conceptos más o menos los podemos ir controlando. Pero cuando trabajamos en equipos donde se abarcan proyectos de gran envergadura, es complicado. Sobre todo si los miembros del grupo no tienen claros estos conceptos.

    En los años 90, Robert C. Martin, hace una recopilación de principios a modo de vía para conseguir que estos desarrollos empresariales alcancen un bajo nivel de acoplamiento, alta cohesión y una fuerte encapsulación. Estos principios se englobaron dentro de las siglas S.O.L.I.D.

    • S: Single Responsibility Principle (SRP) - Principio de Responsabilidad Única
    • O: Open-Closed Principle (OCP) – Principio de Abierto-Cerrado
    • L: Liskov Substitution Principle (LSP) – Principio de Substitución de Liskov
    • I: Interface Segregation Principle (ISP) – Principio de Segregación de Interface
    • D: Dependency Inversion Principle (DIP) – Principio de Inversión de Dependencia

    image

    El Principio de Responsabilidad Única (SRP) nos dice que una clase sólo debe tener una única razón para cambiar. Este principio nos ayuda a dirigir la cohesión y controlar el acoplamiento.

    El Principio de Abierto-Cerrado (OCP) nos indica cómo diseñar clases de manera que estas se puedan extender sin tener que modificar su funcionalidad principal. Aplicando este principio conseguimos sistemas bien encapsulados y altamente cohesivos.

    El Principio de Substitución de Liskov (LSP) se centra en los conceptos de encapsulación y cohesión. Y dice que una clase que implemente una interfaz o herede de una clase base no debe violar la intención o la semántica de la abstracción heredada.

    El Principio de Segregación de la Interfaz (ISP) nos habla de como las clases no deben exponer interfaces que las aplicaciones que las usan no necesitan. Aplicando este principio se obtiene una mejora en la encapsulación y cohesión de sistemas.

    El Principio de Inversión de Dependencias (DIP) está enfocado en hacernos entender de como los detalles de implementación debe depender de la funcionalidad de alto nivel que requiere el sistema y no al revés. Y de como conseguir invertir esta relación de manera satisfactoria.

    Aunque todos los principios SOLID están relacionados entre sí.Voy a dedicar una entrada a profundizar sobre cada uno de estos dando ejemplos muy básicos y explicativos que ayuden a entender la manera en que pueden ser usados en nuestros desarrollos.

    jueves, 22 de abril de 2010

    Principios básicos de la Orientación a Objetos

    A los que sobre todo han ido a la Universidad a estudiar asignaturas de programación con orientación a objetos les sonarán los conceptos de acoplamiento, cohesión y encapsulación. Son conceptos muy fáciles de entender, pero que normalmente muchos desarrolladores no aplican a sus proyectos. Muy probablemente se deba a que estos desarrolladores, a pesar de conocer estos conceptos, no entienden los beneficios de usar diseños con bajos niveles de acoplamiento, alta cohesión y una buena encapsulación de los componentes.

    En esta entrada quiero mostrar las ventajas de usar correctamente estos principios y como nos podría ayudar en os desarrollos que estemos haciendo.

    Acoplamiento

    En desarrollo software, el acoplamiento se puede definir como el grado en el que dos ó más componentes o clases dependen unas de otras.

    Un buen diseño o una buena arquitectura tratará de mantener un nivel de acoplamiento bajo. Me explico con un ejemplo: supongamos que tenemos un software con la siguiente arquitectura de componentes donde se muestran las dependencias que hay entre estos: Acoplamiento Ahora tenemos otro desarrollo distinto, y vemos que una de las funcionalidades ya la realiza el Módulo C del proyecto de la imagen. Perfecto, cogemos ese módulo y lo usamos en nuestro nuevo desarrollo. En seguida nos damos cuenta que es más fácil decirlo que hacerlo. Tiene demasiadas dependencias con otros componentes que a su vez también tienen otras dependencias. Por lo que usar el componente C nos va a suponer también traernos muchos de los otros componentes de los que depende. Es decir, el componente C tiene un grado de acoplamiento muy alto con el resto de componentes.

    “Si un componente tiene un alto grado de acoplamiento con otros componentes, entonces para usar ese componente vas a tener que usar también los otros de los que dependen aunque no los necesites.”

    Se puede reducir el nivel de acoplamiento de un componente de muchas maneras. La más usual es la de usar interfaces o herencia de clases entre los componentes que desacoplen la dependencia entre estos.

    Por el lado contrario, no existen los componentes con nivel de acoplamiento cero. No tienen sentido. Un módulo que no depende de nada y del que nadie depende, ¿qué uso puede tener?

    En definitiva, el acoplamiento no es algo estrictamente malo. Los componentes que forman parte de un sistema deben de poder interactuar unos con otros para que este pueda realizar alguna función. Por tanto, el objetivo es alcanzar un equilibrio en el grado de acoplamiento de los componentes de manera que sea funcional (que haga lo que tiene que hacer), entendible y mantenible por otros desarrolladores.

    Cohesión

    El grado de cohesión de un componente es la relación funcional que existe entre las funciones de ese componente para realizar una tarea. Es decir, un módulo coherente es capaz de realizar una tarea sin necesidad de interactuar con otros componentes.

    Haciendo una extrapolación para sistemas complejos, la cohesión define cómo los distintos componentes que forman parte del sistema se relacionan e interaccionan para crear un sistema que da un valor añadido mayor que el que pueden dar la suma de sus partes.

    “El todo es mayor que la suma de las partes.”

    Este es un concepto mucho más abstracto y quizás más difícil de conseguir debido a que no hay modelos ni recetas mágicas que nos digan como conseguir desarrollar software con un alto grado de cohesión. Sin embargo es un concepto que es bastante usado de la vida cotidiana. Muchas veces hemos oído aplicarlo al deporte: Equipos con alto valor de cohesión consiguen mejores resultados en las competiciones en las que participan que otros menos cohesionados aunque sus jugadores sean mucho mejores. También las empresas buscan esa cohesión: tener equipos de trabajoque trabajen juntos para conseguir productos a tiempo con la mejor calidad posible.

    Supongamos la arquitectura del sistema de la imagen siguiente.Cohesion

    Si se analizan por separado cada uno de los componentes del sistema realmente no se tiene gran cosa. Por separado cada componente realiza una serie de tareas muy limitadas con poco valor por si mismo. Sin embargo, cuando se juntan los componentes y se hace que interaccionen coherentemente entre sí, el valor aportado es muchísimo mayor.

    Para conseguir hacer software con un nivel de cohesión elevado debemos tratar de huir de las mega-clases y mega-componentes que hacen de todo. Es posible que internamente estén bien cohesionados, pero a nivel de sistema no será así. Por ejemplo, que un componente sea capaz de leer los datos de la base de datos, procesarlos y mostrarlos en algún tipo de interfaz de usuario.

    Hay muchos patrones que están enfocados a lograr buenos niveles de cohesión. Por ejemplo el multi-capa (como el del diagrama anterior), MVC, MVVM, etc.

    Encapsulación

    El grado de encapsulación de un componente viene definido por la forma en el que el componente oculta la información superflua y la lógica de lo que hace al resto de componentes del sistema.

    Y es que muchos desarrolladores entiende por encapsulación sólo la ocultación de información, dejando de lado la lógica que los componentes implementan.
    Encapsulación

    Supongamos un diseño en el que la clase X está bien encapsulado. Entre otras ventajas, el hecho de ocultar la lógica hacia el exterior hace que podamos cambiar la implementación interna sin que afecte al resto de clases del diseño. Por otro lado, si hacemos que la funcionalidad de la clase X se base en la implementación de interfaces, los componentes que necesiten usar X se referirán a ella por sus interfaces y no por la clase en sí.

    Por ejemplo, supongamos este par de interfaces sencillas definidas en C#:

    public interface IAnimal
    {
    string Especie { get; }
    void Comer();
    void Andar();
    void Atacar();
    }

    public interface IPersona
    {
    string Nombre { get; set; }
    DateTime FechaNacimiento { get; set; }
    void Trabajar();
    void Hablar();
    }

    public class HombreLobo : IAnimal, IPersona
    {
    #region IAnimal Members

    public string Especie { get { return "Lobo"; } }

    public void Comer()
    {
    // ...
    }

    public void Andar()
    {
    // ...
    }

    public void Atacar()
    {
    // ...
    }

    #endregion

    #region IPersona Members

    public string Nombre {get; set;}

    public DateTime FechaNacimiento {get; set;}

    public void Trabajar()
    {
    // ...
    }

    public void Hablar()
    {
    // ...
    }

    #endregion
    }


    Los componentes que quieran usar esta clase lo pueden hacer basándose en las interfaces que la clase implementa:



    public class MiClase
    {
    public void HacerAlgo()
    {
    IAnimal lobo = new HombreLobo();
    lobo.Atacar();
    lobo.Comer();
    }
    }


    Una correcta encapsulación de las clases o componentes nos ayuda a reducir la duplicación de datos y de procesos en nuestros desarrollos y nos va a ayudar en situaciones donde necesitemos usar alguna funcionalidad proporcionada por alguna de nuestras clases en más de un sitio.



    Para terminar



    Cada uno de los principios que hemos visto están conectados entre sí: El desarrollo de componentes fuertemente encapsulados facilita que consigamos disminuir el nivel de acoplamiento de estos componentes. Y por consecuencia, tener encapsulación y bajo acoplamiento nos ayudará a asegurarnos el tener un sistema altamente cohesionado.