Monday, January 26, 2015

Patrones de diseño de sofware con C# - [0- 30]

Patrones de diseño de software


En el desarrollo de aplicaciones algunos problemas son resueltos de la misma manera una y otra vez de modo que la forma de resolverlos se convierte en un patrón repetitivo.

Esta serie de 30 artículos estará enfocada a explicar y crear algunos de los patrones de diseño de software comunes.

Estos patrones de diseño involucran elementos de la programación orientada a objetos tales como polimorfismo, herencia  y encapsulamiento.

También involucran elementos del principio S.O.L.I.D.

El término S.O.L.I.D. es formado por las iniciales de los siguiente conceptos.

S -  The single responsibility principle (Principio de única responsabilidad)
O -  The open/closed principle (Principio Abierto/Cerrado)
L -  The Liskov substitution principle (El principio de Liskov)
I -  Interface segregation (Segregación de interfaces)
D -  Dependency injection (Inyección de dependencias)


Principio de única responsabilidad

Este principio dicta que se debe escribir código tal que tenga una y solo una razón para cambiar. Si una clase tiene mas de una razón para haceerlo significa que tiene más de una responsabilidad. Esto es que las clases solo deben hacer tareas relacionadas con su propio dominio. Un ejemplo de una clase que tiene más de una responsabilidad es aquella que genera instancias de otra clase dentro de ella misma, de manera que además de crear instancias propias por medio de contructores también crea instancias de otras clases en alguna parte de su código. Otro ejemplo de una clase con muchas responsabilidades es aquella que en un solo método abre una connexion a una base de datos, lee información, procesa datos y finalmente crea un archive de excel con los datos resultantes. Este es un ejemplo de un método con muchas responsabilidades.

Pero... ¿Cuál es el problema?

Si intentamos crear pruebas unitarias sobre el método estaremos hacienda pruebas sobre un proceso muy general y no sobre cada una de las pequeñas responsabilidades. En otras palabras sería major si tuviéramos un método o clase aparte para cada tarea, de modo que podamos crear pruebas unitarias independientes.

Principio Abierto/Cerrado

Nada más claro que la definición de Bertrand Meyer:

Las entidades de software deberían ser abiertas para ser extendidad pero cerradas para ser modificadas.

Esto significa que un módulo o clase deber ser capaz de ser extendida según vayan cambiando los requerimientos pero los cambios realizados no deben resultar en cambios al código fuente o bibliotecas ya compiladas. De manera que no vamos a compilar todo el sistema solo porque un cliente tiene reglas de negocio de envíos diferentes a las reglas comunes para todos.

El principio de Liskov

En términos de programación orientada a objetos esto es:

Si S es un subtipo de T, entonces los objetos de tipo T pueden ser reemplazados por objetos de tipo S sin causar problemas en la ejecución del código. --Barbara Liskov

De modo que si tenenos una clase o interface Persona y una clase Empleado que hereda de persona, esto significa que podemos usar un Empleado en cualquier lugar donde se haga referencia a una Persona.

Segregación de interfaces

De la manera más simple, se refiere a usar interfaces que contengan métodos únicos que sirvan para un único propósito o por lo menos solo unos pocos estrechamente relacionados.

Tomemos como ejemplo la siguiente interface:

interface IModificable
{
  public void Agregar();
  public void Borrar();
  public void Cambiar();
  public void Desplegar();
}

Esta interface tiene cuatro responsabilidades, agregar, borrar, cambiar y desplegar. El problema se presenta cuando no queremos que cierta clase que implementa esta interface sea borrable.
Veamos ahora este ejemplo:

interface IAgregable
{  public void Agregar();
}
interface IBorrable
{  public void Borrar();
}
interface ICambiable
{  public void Cambiar();
}
interface IDesplegable
{  public void Desplegar();
}


El conjunto de interfaces arriba mostrado separan la funcionalidad en grupos independientes. El ejemplo exagera un poco la separación pero la idea se muestra.

Inyección de dependencias


Cuando una clase hace uso de una instancia de otra clase para realizar tareas, es necesario que haya un constructor que crea la segunda.
Un buen ejemplo es cuando se crea una aplicación y se hace uso de un repositorio de datos:

class Program
{
  static void Main(string[] args)
  {
    IServicioPersonas servicio = new RepositorioDeDatosDePersonas();
  }
} 

Para que RepositorioDeDatosDePersonas pueda ser utilizado es necesario hacer una referencia a la biblioteca que contenga esa clase. Esto no sería problema a menos que queramos que nuestro código sea adaptable y escalable. Esto se logra con algún mecanismo que nos permita crear instancias de RepositorioDeDatosDePersonas sin necesidad de hacer referencia a su biblioteca contenedora al momento de la compilación. Así que lo que se hace necesario es crear instancias de RepositorioDeDatosDePersonas al vuelo de manera que nuestro código cambie a algo como:


class Program
{
  static void Main(string[] args)
  {
 
    IServicioPersonas servicio =
        new InyectorDeCodigo<IServicioPersonas>().ObtenerInstancia();  }
} 

   

Los patrones


Los 23 patrones de diseño más conocidos están contenidos en tres grupos y se enumeran a continuación:

Estructurales

  • Decorator
  • Proxy
  • Bridge
  • Composite
  • Flyweight
  • Adapter
  • Façade

De construcción

  • Prototype
  • Factory method
  • Singleton
  • Abstract factory
  • Builder

De comportamiento

  • Strategy
  • State
  • Template methos
  • Chain of responsability
  • Command
  • Iterator
  • Mediator
  • Observer
  • Visitor
  • Interpreter
  • Memento

Además de los aquí mencionadoes existen otros como Null, Asymmetric, Entourage, Two-Layers, Three-Layers, Stairway, CQRS and Event Sourcing,

Algunos de los patrones antes mencionados se han convertido en anti-patrones o sea, formas de resolver un problema que acaba complicándolo en vez de ser una solución.

Otros son combinaciones o formas de agrupar el código de manera que sea escalable.

Estos y otros detalles se irán revisando en los siguientes artículos de esta serie.


Referencias

C# 3.0 Design Patterns

Adaptive Code via C#:

Architecting Applications for the Enterprise, 2nd Edition

CQRS Journey

Developer's Guide to Dependency Injection Using Unitypatterns & practices Developer Center


Jesus.MT