6 de enero de 2012

Usando Rule Actions en subscripciones de Windows Azure Service Bus

La versión en inglés de este blog post puede ser encontrada en http://blogs.southworks.net/jrowies/2012/01/06/using-rule-actions-in-windows-azure-service-bus-subscriptions/

Asignar rule actions a subscripciones de Service Bus es un mecanismo útil para modificar automáticamente las propiedades de los BrokeredMessages cuando los mismos son enviados a un Topic.

La forma más sencilla de asignar una rule action a una subscripción es hacerlo cuando la subscripción está siendo creada:

namespaceManager.CreateSubscription(
    topicName, subscriptionName,
    new RuleDescription { Action = new SqlRuleAction("set dcount=[sys].DeliveryCount") });
Aquí estamos asignado el valor que tiene la propiedad DeliveryCount del objeto BrokeredMessage a una propiedad del encabezado del mensaje (las propiedades del encabezado del mensaje pueden ser utilizadas para enrutar los mismos a determinadas subscripciones). Si necesitamos asignar múltiples rule actions a una única subscripción, primero tenemos que crear la subscripción y luego crear las rule actions y asignarlas a la subscripción:
namespaceManager.CreateSubscription(topicName, subscriptionName);

var deliveryCountRule = new RuleDescription()
{
    Action = new SqlRuleAction("set dcount=[sys].DeliveryCount"),
    Name = "DeliveryCountRule"
};

var sizeRule = new RuleDescription()
{
    Action = new SqlRuleAction("set sz=[sys].Size"),
    Name = "SizeRule"
};

var subscriptionClient = messagingFactory.CreateSubscriptionClient(
    topicName, subscriptionName);

subscriptionClient.AddRule(deliveryCountRule);
subscriptionClient.AddRule(sizeRule);
Podemos combinar rule actions con filtros para asignar valores a propiedades, basándonos en condiciones lógicas:
namespaceManager.CreateSubscription(topicName, subscriptionName);

var ruleLowPrice = new RuleDescription()
{
    Action = new SqlRuleAction("set PriceRange='Low'"),
    Filter = new SqlFilter("TotalCost < 100"),
    Name = "LowPrice"
};

var ruleMediumPrice = new RuleDescription()
{
    Action = new SqlRuleAction("set PriceRange='Medium'"),
    Filter = new SqlFilter("TotalCost >= 100 AND TotalCost < 500"),
    Name = "MediumPrice"
};

var ruleHighPrice = new RuleDescription()
{
    Action = new SqlRuleAction("set PriceRange='High'"),
    Filter = new SqlFilter("TotalCost >= 500"),
    Name = "HighPrice"
};

var subscriptionClient = messagingFactory.CreateSubscriptionClient(
    topicName, subscriptionName);

subscriptionClient.AddRule(ruleLowPrice);
subscriptionClient.AddRule(ruleMediumPrice);
subscriptionClient.AddRule(ruleHighPrice);
Ya casi estamos... La última porción de código debería funcionar, salvo por el hecho de que cuando una subscripción es creada usando el método CreateSubscription, una regla por defecto con un TrueFilter es agregada, lo cual significa que todo mensaje va a aparecer en esa subscripción. Para evitar esto, debemos eliminar las reglas pre-existentes antes de agregar las nuestras.
var rules = namespaceManager.GetRules(topicName, subscriptionName);

...

var subscriptionClient = messagingFactory.CreateSubscriptionClient(
    topicName, subscriptionName);

foreach (var rule in rules)
    subscriptionClient.RemoveRule(rule.Name);

subscriptionClient.AddRule(ruleLowPrice);
subscriptionClient.AddRule(ruleMediumPrice);
subscriptionClient.AddRule(ruleHighPrice);

...
Información más detallada sobre las expresiones que pueden ser utilizadas al definir rule actions puede ser encontrada en http://msdn.microsoft.com/en-us/library/microsoft.servicebus.messaging.sqlruleaction.sqlexpression.aspx

8 de diciembre de 2011

Principio de Inversión de Dependencias - SOLID

Para finalizar con la serie de posts sobre los principios SOLID, luego de unos meses bastante ajetreados que incluyeron una variedad de cambios (cambio de trabajo, cambio de pañales, cambio de sueño... :P) en esta oportunidad vamos a ver el principio DIP, Dependency Inversion Principle.

Este principio intenta solucionar algunos de los problemas más comunes del diseño orientado a objetos.

Rigidez: los cambios afectan a demasiadas partes del sistema
Fragilidad: cada cambio genera problemas en lugares inesperados
Inmobilidad: imposibilidad de reutilización

La definición del principio dice lo siguiente:

A. Módulos de niveles superiores no deben depender de módulos de niveles inferiores. Ambos deben depender de abstracciones.
B. Las abstracciones no deben depender de los detalles. Los detalles deben depender de las abstracciones.


Un ejemplo de un diseño que se vería beneficiado aplicando DIP es el siguiente:

Supongamos que tenemos una aplicación de ventas online hecha con ASP.NET MVC. Esta aplicación tiene un controlador con una acción que es ejecutada cuando el usuario confirma una compra. A su vez, cuando el usuario confirma la compra, se le envía un email con información sobre la misma.

public class ShoppingCartController : Controller
{

    public ActionResult Checkout()
    {
        ...
 
        customer = ...
        order = ...

        var emailSender = new EmailSender(server: "smtp.myserver.com");
        emailSender.SendOrderConfirmed(customer, order);

        ...
    }

}


Imaginemos que pasa el tiempo y tengo que modificar la clase EmailSender para poder especificar, además del servidor, el puerto que se va a utilizar para enviar los emails.

    

    var emailSender = new EmailSender(server: "smtp.myserver.com", port: 255);



Esto hace evidente el primer problema de nuestro diseño, estamos ante un diseño rígido, ya que el cambio en la clase EmailSender me obliga a modificar todos los lugares de mi aplicación donde la esté utilizando, caso contrario, mi aplicación no compilará.

Una posible solución es dejar el constructor de EmailSender tal como está actualmente y agregar una propiedad Port para poder indicar el puerto a utilizar al enviar mails.

    var emailSender = new EmailSender(server: "smtp.myserver.com");
    emailSender.Port = 255;



Este nuevo diseño tiene otro problema, es frágil, ya que si utilizo la clase EmailSender en varios lugares de mi aplicación, fácilmente puedo olvidar asignar el valor correcto a la propiedad Port, lo cual resultará en una aplicación que no siempre se comportará como es esperado (en algunos casos el envío de mails funcionará por tener el puerto asignado correctamente y en otros no, por utilizar el puerto default).

Para solucionar esto podemos mover la creación de la clase EmailSender a un lugar centralizado, y pasarle una instancia ya creada y correctamente configurada al controller:

    public ShoppingCartController(EmailSender emailSender)
    {
        this.emailSender = emailSender;
    }



La acción de pasarle al controlador sus dependencias, en este caso una instancia de EmailSender, es llamado inyección de dependencias (en inglés, dependency injection ó simplemente DI).

La inyección de dependencias puede hacerse por constructor, como es en este caso, o también por propiedades. Por lo general la inyección por constructor es la práctica más recomendable.

Vale la pena mencionar que la inyección de dependencias se puede hacer en forma manual o mediante algún contenedor de inversión de control (en inglés, inversion of control container ó ioc container). Algunos ioc containers son Castle Windsor, Unity o StructureMap, por mencionar algunos.

Hasta acá venimos bien salvo por el hecho que este diseño tiene otro problema, la "inmovilidad", ya que si quisiéramos agregar un nuevo tipo de mecanismo para enviar notificaciones, por ejemplo por SMS, no podríamos reutilizar el controlador tal cual está ya que el mismo depende de EmailSender.

Este problema puede ser solucionado aplicando el principio de inversión de dependencias, para esto debemos crear una abstracción de EmailSender (interfaz IMessageSender) y de esa manera podremos "invertir" la dependencia:





Esto es llamado inversión de dependencias (o inversión de control) ya que se podría decir que la dirección de la dependencia entre ShoppingCartController y EmailSender, al introducir la abstracción IMessageSender, fué invertida.

Finalmente, podemos ver que nuestro diseño final cumple DIP ya que:

A. El controller (módulo de nivel superior) ya no depende de EmailSender (módulo de nivel inferior) y tanto el controller como el EmailSender dependen de IMessageSender (una abstracción)
B. El controller y EmailSender (detalles) dependen de IMessageSender (una abstracción)

En mi primer post sobre el principio SRP podrán encontrar varios enlaces a las lecturas recomendadas sobre principios SOLID.

Espero que sea de utilidad, cualquier comentario será bienvenido.

Posts anteriores sobre principios SOLID
Principio de responsabilidad única - SRP
Principio abierto-cerrado - OCP
Principio de Substitución de Liskov - LSP
Principio de Separación de Interfaces - ISP

28 de abril de 2011

Principio de Separación de Interfaces - SOLID

Siguiendo con los principios SOLID, en esta oportunidad vamos a ver el principio ISP, Interface Segregation Principle.

Comencemos por la definición:

Una clase cliente no debe ser forzada a depender de interfaces que no usa.

Vamos a explicar el concepto de ISP utilizando el ejemplo de Daos de sólo lectura del post anterior sobre LSP.

Supongamos que tenemos una interfaz IDao como la siguiente:

public interface IDao
{
void Insert(object entity);
void Update(object id, object entity);
void Delete(object id);
object[] GetAll();
object GetById(object id);
}


Y una clase FacturaDao que implementa IDao

public class FacturaDao : IDao
{
public void Insert(object entity)
{
//se inserta una factura
}

public void Update(object id, object entity)
{
//se actualiza una factura
}

public void Delete(object id)
{
//se elimina una factura
}

public object[] GetAll()
{
//se obtienen todas las facturas
}

public object GetById(object id)
{
//se obtiene una factura por id
}
}


Las clases que actúen como clientes de FacturaDao, dependerán de la interfaz IDao.

En el caso que quisiéramos crear una capa de acceso a datos de solo lectura, podríamos definir este otro Dao:

public class FacturaDaoReadOnly : IDao
{
public void Insert(object entity)
{
throw new DaoReadOnlyException();
}

public void Update(object id, object entity)
{
throw new DaoReadOnlyException();
}

public void Delete(object id)
{
throw new DaoReadOnlyException();
}

public object[] GetAll()
{
//se obtienen todas las facturas
}

public object GetById(object id)
{
//se obtiene una factura por id
}
}


De esta manera, las clases cliente de FacturaDaoReadOnly, también estarán dependiendo de la interfaz IDao.

Aquí es donde no se cumple por primera vez el principio ISP, ya que una capa de solo lectura no tiene porqué depender de una interfaz IDao que contiene métodos como Insert, Update y Delete. Esto no es recomendable ya que le quita claridad a nuestro diseño.

Por otra parte, también esto trae aparejado el problema de que mi clase FacturaDaoReadOnly tuvo que implementar los métodos Insert, Update y Delete lanzando una excepción DaoReadOnlyException. Lo cual, nuevamente, no es recomendable ya que si a futuro se agregan nuevos métodos a IDao (ej. DeleteAll), si no lo implemento en FacturaDaoReadOnly la misma no va a compilar.

Cómo podemos solucionar esto? Separando la interfaz de lectura de la interfaz de lectura/escritura:

public interface IDaoLectura
{
object[] GetAll();
object GetById(object id);
}


public interface IDao : IDaoLectura
{
void Insert(object entity);
void Update(object id, object entity);
void Delete(object id);
}


public class FacturaDao : IDao
{
public void Insert(object entity)
{
//se inserta una factura
}

public void Update(object id, object entity)
{
//se actualiza una factura
}

public void Delete(object id)
{
//se elimina una factura
}

public object[] GetAll()
{
//se obtienen todas las facturas
}

public object GetById(object id)
{
//se obtiene una factura por id
}
}


public class FacturaDaoReadOnly : IDaoLectura
{
public object[] GetAll()
{
//se obtienen todas las facturas
return null;
}

public object GetById(object id)
{
//se obtiene una factura por id
return null;
}
}


De esta forma estaremos cumpliendo el principio de separación de interfaces.

En mi primer post sobre el principio SRP podrán encontrar varios enlaces a las lecturas recomendadas sobre principios SOLID.

Espero que sea de utilidad, cualquier comentario será bienvenido.

Posts anteriores sobre principios SOLID
Principio de responsabilidad única - SRP
Principio abierto-cerrado - OCP
Principio de Substitución de Liskov - LSP

21 de enero de 2011

Principio de Substitución de Liskov - SOLID

Siguiendo con la serie sobre principios SOLID, hoy nos toca ver el principio LSP, Liskov Substitution Principle.

LSP es probablemente el principio SOLID más difícil de explicar ya que los ejemplos de la "vida real" que uno suele encontrar son escasos o muchas veces parecen describir mejor el principio OCP que LSP.

Comencemos por la definición del principio:

Métodos que referencian a clases base, deben poder utilizar objetos de clases derivadas sin necesidad de saberlo.

El ejemplo del Cuadrado y el Rectángulo

Dicho esto, vamos a empezar explicando LSP con el típico ejemplo de libro: el caso del cuadrado y el rectángulo.

Supongamos que tenemos una clase Rectangulo como la siguiente:


public class Rectangulo
{
private int _ancho;
private int _alto;

public virtual void AsignarAncho(int ancho)
{
_ancho = ancho;
}

public virtual void AsignarAlto(int alto)
{
_alto = alto;
}

public int ObtenerAncho()
{
return _ancho;
}

public int ObtenerAlto()
{
return _alto;
}

public int CalcularArea()
{
return _ancho * _alto;
}
}


Luego decidimos que un Cuadrado "es-un" rectángulo y por eso creamos la clase Cuadrado como una subclase de Rectangulo, y para asegurarnos que la forma realmente sea la de un cuadrado, hacemos override de AsignarAlto y de AsignarAncho de forma tal que siempre el alto y el ancho sean iguales.


public class Cuadrado : Rectangulo
{
public override void AsignarAncho(int ancho)
{
base.AsignarAncho(ancho);
base.AsignarAlto(ancho);
}

public override void AsignarAlto(int alto)
{
base.AsignarAlto(alto);
base.AsignarAncho(alto);
}
}


Hasta acá todo parecería correcto, pero veamos qué pasa cuando tenemos una tercer clase, en este caso un test unitario, con el siguiente código:


[TestFixture]
public class Test
{
[Test]
public void TestCalculoAreaRectangulo()
{
testAreaFiguraRectangular(new Rectangulo());
}

[Test]
public void TestCalculoAreaCuadrado()
{
testAreaFiguraRectangular(new Cuadrado());
}

private void testAreaFiguraRectangular(Rectangulo rect)
{
rect.AsignarAlto(10);
rect.AsignarAncho(2);
Assert.AreEqual(20, rect.CalcularArea());
}
}


Cuando la variable rectangulo sea una instancia de la clase Cuadrado este test va a fallar (el area del cuadrado va a ser 2*2=4 en lugar de 2*10=20).

Esto ocurre porque se ha violado el principio LSP. Nuestro test referencia a la clase base Rectangulo y al utilizar una instancia de Cuadrado (clase derivada de Rectangulo) el test comienza a fallar.

Sub-tipos vs Sub-clases y Diseño por Contrato

Una vez planteado el ejemplo viene bien mencionar un par de conceptos que ayudan a entender un poco mas cuándo se puede estar violando LSP y cuándo no.

Barbara Liskov (de ahí el nombre del principio) definió como sub-tipo a la propiedad que tiene un objeto A de poder reemplazar a otro objeto B sin que el comportamiento de los objetos que los utilizan deba ser modificado (A es un sub-tipo de B)

En nuestro ejemplo el Cuadrado no puede reemplazar al Rectangulo ya que el comportamiento de nuestro test debería ser modificado para que siga funcionando correctamente, probablemente haciendo algo así:


private void testAreaFiguraRectangular(Rectangulo rect)
{
rect.AsignarAlto(10);
rect.AsignarAncho(2);

if (rect is Cuadrado)
Assert.AreEqual(4, rect.CalcularArea());
else
Assert.AreEqual(20, rect.CalcularArea());
}


De aquí se deduce que el Cuadrado puede ser una sub-clase del Rectangulo, pero no es un sub-tipo, según la definición de Liskov. Por lo tanto, que una clase herede de otra no nos asegura el principio LSP.

Otro concepto que vale la pena mencionar es el de diseño por contrato, que se puede resumir en que los métodos de una clase declaran pre-condiciones y post-condiciones donde las pre-condiciones se deben cumplir para que el método se ejecute y, luego de la ejecución, las post-condiciones deben ser cumplidas también.

Al redefinir métodos de una clase, para no violar LSP, se deben respetar tanto las pre-condiciones como las post-condiciones definidas en la clase base, es decir, el comportamiento de una clase no debe romper ninguna de las restricciones impuestas por la clase base.

En el ejemplo del rectangulo y el cuadrado, las post-condiciones del método AsignarAncho definidas en la clase base Rectangulo son las siguientes:

  • "El ancho tiene el nuevo valor asignado"
  • "El alto se mantiene con el mismo valor que tenía antes"

La segunda post-condición no se respeta en la redefinición del método AsignarAncho de la clase Cuadrado, ya que ante un cambio en el ancho, también se modifica el alto en forma automática.

Un caso donde no se respetarían las pre-condiciones sería por ejemplo que en AsignarAncho de Cuadrado se haga algo así:


public override void AsignarAncho(int ancho)
{
if (ancho == 0)
throw new Exception("El ancho debe ser distinto de 0");

base.AsignarAncho(ancho);
base.AsignarAlto(ancho);
}


En este caso, estamos pidiendo que el ancho sea distinto de cero como pre-condición de AsignarAncho en Cuadrado, mientras que eso no se exige en la clase Rectangulo.

LSP y las interfaces

Por último vale la pena mencionar qué pasa con las interfaces. Si en lugar de heredar de una clase base con comportamiento ya definido, se implementa una interfaz, hay pre-condiciones y post-condiciones que respetar? La respuesta es: en la mayoría de los casos si las hay, y por lo general son implícitas y se desprenden del sentido común.

Por ejemplo, si en lugar de tener la clase Rectangulo, tengo una interfaz IFiguraRectangular, y dos clases que la implementan, Rectangulo y Cuadrado. Algo de lo mencionado previamente deja de tener vigencia? No sería lógico que si una clase interactúa con una variable de tipo IFiguraRectangular, la cual tiene sus métodos AsignarAncho y AsignarAlto, pueda asumir que el alto y el ancho se asignan por separado? Yo creo que sí...

Un ejemplo de la vida real

Otro ejemplo, un poco distinto, que a mi entender es mucho más visto en la vida real es el siguiente.

Supongamos que tenemos una interfaz para mis Daos como la siguiente:


public interface IDao
{
void Insert(object entity);
void Update(object id, object entity);
void Delete(object id);
object[] GetAll();
object GetById(object id);
}


Luego decido crear un Dao para mis Facturas


public class FacturaDao : IDao
{
public void Insert(object entity)
{
//se inserta una factura
}

public void Update(object id, object entity)
{
//se actualiza una factura
}

public void Delete(object id)
{
//se elimina una factura
}

public object[] GetAll()
{
//se obtienen todas las facturas
}

public object GetById(object id)
{
//se obtiene una factura por id
}
}


Hasta ahí todo bien, salvo que luego, por algún motivo necesito crear una capa de acceso a datos donde sólo pueda permitir lecturas, entonces mi Dao para Facturas debería ser como el siguiente:


public class FacturaDaoReadOnly : IDao
{
public void Insert(object entity)
{
throw new DaoReadOnlyException();
}

public void Update(object id, object entity)
{
throw new DaoReadOnlyException();
}

public void Delete(object id)
{
throw new DaoReadOnlyException();
}

public object[] GetAll()
{
//se obtienen todas las facturas
}

public object GetById(object id)
{
//se obtiene una factura por id
}
}


Aquí el problema que se presenta es que las clases que interactúen con IDao, van a tener que tener en cuenta que en algunos casos los métodos Insert, Update y Delete pueden lanzar una excepción DaoReadOnlyException, lo cual viola el principio de Liskov por los siguientes motivos:

  • No se estarían cumpliendo las post-condiciones de los métodos Insert, Update y Delete porque se debe contemplar que el Dao puede lanzar una excepción DaoReadOnlyException, lo cual es una post-condición particular de una implementación concreta del Dao, en este caso el dao readonly de Facturas.
  • No se estaría cumpliendo el concepto de diseño por contrato, ya que en ninguna parte de la interfaz IDao se sugiere que un Dao puede ser readonly.

Una posible forma de solucionar esto sería mediante la separación en distintas interfaces de las distintas acciones del Dao, por ejemplo IDaoLectura, IDaoEscritura, pero esto es algo que vamos a cubrir cuando veamos el principio de segregación de interfaces (ISP).

Conclusión

Por último y resumiendo, no son demasiadas las aplicaciones en la "vida real" que se le pueden dar al principio LSP, pero casos como el anteriormente descripto (o ligeras variaciones del mismo) se ven muy seguido, por lo tanto es un principio que vale la pena tener bien presente.

En mi primer post sobre el principio SRP podrán encontrar varios enlaces a las lecturas recomendadas sobre principios SOLID.

Espero que sea de utilidad, cualquier comentario será bienvenido.

Posts anteriores sobre principios SOLID
Principio de responsabilidad única - SRP
Principio abierto-cerrado - OCP

18 de noviembre de 2010

Principio Abierto-Cerrado - SOLID

Continuando con la serie prometida de posts sobre principios SOLID, hoy voy a presentar el principio OCP, Open-Closed Principle.

La definición del principio dice lo siguiente:

Las entidades de software (clases, módulos, métodos, etc.) deben estar abiertas para extensión, pero cerradas para modificación.

Dicho de otra forma, esas entidades deben estar diseñadas de tal manera que su comportamiento pueda ser alterado o extendido sin modificar el código fuente, logrando así que la entidad de software quede protegida contra el cambio.

Dicho así puede sonar extraño, pero para entender más fácilmente este principio hay que tener en cuenta dos cosas, la primera es la más obvia y es qué es lo que quiero proteger?, todo un assembly, una clase determinada o un método particular? y la segunda y no tan obvia es, de qué lo quiero proteger?

Voy a presentar un ejemplo basado en la clase Factura de mi post previo sobre SRP que, espero, sirva para aclarar un poco.

Supongamos que mi clase Factura tiene un método Enviar que se encarga de enviar una copia de la factura al cliente y otra copia al sector de administración, pero estas copias son enviadas de distintas formas (por correo, por email, por fax, etc.)

Por un lado tengo un enumerado que me indica el método de envío:


public enum MetodoEnvio
{
eMail,
Correo,
Fax
}


Por otro lado, siguiendo el principio de responsabilidad única (SRP) tengo clases que se encargan de realizar el envío, los despachadores:


public class CorreoDespachadorFacturas
{
public CorreoDespachadorFacturas(Destino destino)
{
// guardar destino en un field
}

public void Despachar(Factura factura)
{
throw new NotImplementedException();
}
}

public class EMailDespachadorFacturas
{
public EMailDespachadorFacturas(Destino destino)
{
// guardar destino en un field
}

public void Despachar(Factura factura)
{
throw new NotImplementedException();
}
}

public class FaxDespachadorFacturas
{
public FaxDespachadorFacturas(Destino destino)
{
// guardar destino en un field
}

public void Despachar(Factura factura)
{
throw new NotImplementedException();
}
}


Y finalmente tengo mi clase Factura y un ejemplo simple para invocar al método Enviar de la factura:


public class Factura
{
public void Enviar(MetodoEnvio metodoEnvioCopiaCliente, Destino destinoCopiaCliente,
MetodoEnvio metodoEnvioCopiaAdministracion, Destino destinoCopiaAdministracion)
{
enviarCopia(metodoEnvioCopiaCliente, destinoCopiaCliente);
enviarCopia(metodoEnvioCopiaAdministracion, destinoCopiaAdministracion);
}

private void enviarCopia(MetodoEnvio metodoEnvio, Destino destino)
{
switch (metodoEnvio)
{
case MetodoEnvio.eMail:
var despachadorEMail = new EMailDespachadorFacturas(destino);
despachadorEMail.Despachar(this);
break;
case MetodoEnvio.Correo:
var despachadorCorreo = new CorreoDespachadorFacturas(destino);
despachadorCorreo.Despachar(this);
break;
case MetodoEnvio.Fax:
var despachadorFax = new FaxDespachadorFacturas(destino);
despachadorFax.Despachar(this);
break;
}
}

//..
}

public class EjemploUsoFactura
{
public void EnviarFacturas(IEnumerable<factura> facturas)
{
foreach (var factura in facturas)
factura.Enviar(MetodoEnvio.Correo, new Destino { /* domicilio del cliente */ },
MetodoEnvio.eMail, new Destino { /* domicilio de la administracion */ });
}

//...
}


Respondamos las dos preguntas planteadas más arriba:

1) Qué quiero proteger?

Quiero proteger el método Enviar de la clase Factura

2) De qué quiero proteger al método Enviar?

Para soportar nuevos métodos de envío que se agreguen en el futuro, tal como está diseñado el método Enviar, debería abrir su código y agregar un nuevo case al switch, por lo tanto no estaría cumpliendo OCP, por lo tanto la respuesta sería "Quiero protegerlo ante el agregado de nuevos métodos de envío"

Una forma de hacer esto es creando una interfaz IDespachadorFacturas y haciendo que esta interfaz sea implementada por todos mis despachadores concretos:


public interface IDespachadorFacturas
{
void Despachar(Factura factura);
}

public class CorreoDespachadorFacturas : IDespachadorFacturas
{
public CorreoDespachadorFacturas(Destino destino)
{
// guardar destino en un field
}

#region IDespachadorFacturas Members

public void Despachar(Factura factura)
{
throw new NotImplementedException();
}

#endregion
}

public class EMailDespachadorFacturas : IDespachadorFacturas
{
public EMailDespachadorFacturas(Destino destino)
{
// guardar destino en un field
}

#region IDespachadorFacturas Members

public void Despachar(Factura factura)
{
throw new NotImplementedException();
}

#endregion
}

public class FaxDespachadorFacturas : IDespachadorFacturas
{
public FaxDespachadorFacturas(Destino destino)
{
// guardar destino en un field
}

#region IDespachadorFacturas Members

public void Despachar(Factura factura)
{
throw new NotImplementedException();
}

#endregion
}


De esta forma nos abstraemos de cuál sea el método de envío ya que simplemente desde la Factura "hablaremos" con la interfaz IDespachadorFacturas:


public class Factura
{
public void Enviar(IDespachadorFacturas despachadorFacturaCopiaCliente,
IDespachadorFacturas despachadorFacturaCopiaAdministracion)
{
despachadorFacturaCopiaCliente.Despachar(this);
despachadorFacturaCopiaAdministracion.Despachar(this);
}

//...

}

public class EjemploUsoFactura
{
public void EnviarFacturas(IEnumerable<factura> facturas)
{
IDespachadorFacturas despachadorCopiaCliente =
new CorreoDespachadorFacturas(new Destino { /* domicilio del cliente */ });
IDespachadorFacturas despachadorCopiaAdministracion =
new EMailDespachadorFacturas(new Destino { /* domicilio de la administracion */ });

foreach (var factura in facturas)
factura.Enviar(despachadorCopiaCliente, despachadorCopiaAdministracion);
}

//...
}


Podríamos terminar aquí ya que hemos cumplido en proteger al método Enviar del agregado a futuro de nuevos métodos de envío, pero miremos un poco más como ha quedado nuestro diseño:


public void Enviar(IDespachadorFacturas despachadorFacturaCopiaCliente,
IDespachadorFacturas despachadorFacturaCopiaAdministracion)
{
despachadorFacturaCopiaCliente.Despachar(this);
despachadorFacturaCopiaAdministracion.Despachar(this);
}


Qué pasaría si en el futuro se presenta la necesidad de enviar una tercer copia de la factura a Tesorería? el método Enviar no está protegido contra este cambio, ya que tendría que abrir el código fuente del método y agregar algo así:


public void Enviar(IDespachadorFacturas despachadorFacturaCopiaCliente,
IDespachadorFacturas despachadorFacturaCopiaAdministracion,
IDespachadorFacturas despachadorFacturaCopiaTesoreria)
{
despachadorFacturaCopiaCliente.Despachar(this);
despachadorFacturaCopiaAdministracion.Despachar(this);
despachadorFacturaCopiaTesoreria.Despachar(this);
}


Para solucionar este problema podríamos hacer lo siguiente:


public class Factura
{
public void Enviar(IEnumerable<idespachadorfacturas> despachadoresFacturas)
{
foreach (var despachador in despachadoresFacturas)
despachador.Despachar(this);
}

//...
}

public class EjemploUsoFactura
{
public void EnviarFacturas(IEnumerable<factura> facturas)
{
IDespachadorFacturas despachadorCopiaCliente =
new CorreoDespachadorFacturas(new Destino { /* domicilio del cliente */ });
IDespachadorFacturas despachadorCopiaAdministracion =
new EMailDespachadorFacturas(new Destino { /* domicilio de la administracion */ });
IDespachadorFacturas despachadorCopiaTesoreria =
new FaxDespachadorFacturas(new Destino { /* domicilio de la tesoreria */ });

var despachadores = new List<idespachadorfacturas>
{ despachadorCopiaCliente, despachadorCopiaAdministracion, despachadorCopiaTesoreria };
foreach (var factura in facturas)
factura.Enviar(despachadores);
}

//...
}


Y de esta forma tendremos protegido al método Enviar de la factura ante dos posibles cambios:
  • El agregado de nuevos métodos de envío
  • La necesidad de enviar más copias a otros destinatarios.

Pero todavía hay un poco más, hay otra forma de solucionar este último problema que se nos presentó y es aplicando un patrón de diseño llamado Composite.

Creamos un nuevo despachador (el Composite) que va a contener todos los despachadores que queramos utilizar para enviar facturas, y pasamos ese despachador al método Enviar.

Luego será el despachador composite el encargado de llamar al método Despachar de cada despachador:


public class CompositeDespachadorFacturas : IDespachadorFacturas
{
public void AgregarDespachador(IDespachadorFacturas despachador)
{
_despachadores.Add(despachador);
}

public void EliminarDespachador(IDespachadorFacturas despachador)
{
throw new NotImplementedException();
}

private readonly IList<idespachadorfacturas> _despachadores = new List<idespachadorfacturas>();

#region IDespachadorFacturas Members

void IDespachadorFacturas.Despachar(Factura factura)
{
foreach (IDespachadorFacturas despachador in _despachadores)
despachador.Despachar(factura);
}

#endregion
}



public class Factura
{
public void Enviar(IDespachadorFacturas despachadorFacturas)
{
despachadorFacturas.Despachar(this);
}

//...
}

public class EjemploUsoFactura
{
public void EnviarFacturas(IEnumerable<factura> facturas)
{
CompositeDespachadorFacturas composite = new CompositeDespachadorFacturas();

IDespachadorFacturas despachadorCopiaCliente =
new CorreoDespachadorFacturas(new Destino { /* domicilio del cliente */ });
composite.AgregarDespachador(despachadorCopiaCliente);

IDespachadorFacturas despachadorCopiaAdministracion =
new EMailDespachadorFacturas(new Destino { /* domicilio de la administracion */ });
composite.AgregarDespachador(despachadorCopiaAdministracion);

IDespachadorFacturas despachadorCopiaTesoreria =
new FaxDespachadorFacturas(new Destino { /* domicilio de la tesoreria */ });
composite.AgregarDespachador(despachadorCopiaTesoreria);

foreach (var factura in facturas)
factura.Enviar(composite);
}

//...
}


Cuál es la ventaja de esta última solución? de esta forma el método enviar de la factura se abstrae al máximo posible de la forma en que se despachan las mismas, es decir, la factura sabe que tiene que hablar con un despachador que tiene un único método Despachar, luego la forma en que se despache la factura, los destinos a los cuáles será enviada y también la cantidad de veces que la misma será despachada es totalmente transparente para la factura.
Esto minimiza la posibilidad de que en el futuro algún cambio a la funcionalidad de mi aplicación impacte de forma tal, que tenga que abrir el código del método enviar de la factura y modificarlo.

Por último, es importante destacar que la decisión de aplicar OCP sobre un método, clase, etc. debe ser una decisión estratégica basándonos en nuestra experiencia, ya que sería imposible intentar cerrar por completo nuestras entidades de software. Será en aquellos casos donde se detecte que hay una gran probabilidad de cambio donde tendremos que aplicar OCP para mejorar la mantenibilidad de nuestro sistema.

En mi post anterior sobre el principio SRP podrán encontrar varios enlaces a las lecturas recomendadas sobre principios SOLID.

Espero que sea de utilidad, cualquier comentario será bienvenido.

18 de junio de 2010

Principio de responsabilidad única - SOLID

Los principios SOLID son una serie de herramientas o técnicas introducidas por Robert C. Martin a principios de 2000, los cuales aplicados en conjunto deberían llevar a obtener diseños que sean mas mantenibles, flexibles y extensibles.

SOLID es un acrónimo formado por la primer letra del nombre de cada uno de los cinco principios, los cuales son:

SRP, alias Single responsibility principle, alias Principio de responsabilidad única

OCP, alias Open/closed principle, alias Principio de abierto/cerrado

LSP, alias Liskov substitution principle, alias Principio de substitución de Liskov

ISP, alias Interface segregation principle, alias Principio de separación de interfaces

DIP, alias Dependency inversion principle, alias Principio de inversión de dependencias

Para obtener el mayor beneficio de estos principios, los mismos se deberían aplicar en conjunto, ya que unos refuerzan a otros (algo así como las prácticas de extreme programming, las cuales también se refuerzan entre sí)

Como por algún lado hay que empezar, en este primer post vamos a tratar el principio de responsabilidad única (SRP)

La definición del principio de responsabilidad única dice lo siguiente:

Nunca debería haber más de un motivo por el cual una clase deba cambiar

Que significa esto? si pensamos en el concepto de responsabilidad como tareas que se le asignan a una clase, imaginemos que tenemos una clase con mas de una tarea por realizar, es más, imaginemos que tiene muchas tareas que realizar, por ejemplo pensemos en una clase Factura donde cada método de la misma es una tarea que la Factura realiza:



NOTA: claro que a esta clase Factura le falta información básica como fecha, número, etc... pero para este ejemplo vamos a obviar esta información

Ahora pensemos qué pasa si queremos cambiar la forma en que se imprime la Factura, esto implica cambiar mi clase Factura, que pasaría si quiero cambiar la forma en que se calculan los descuentos o los impuestos, también esto implica cambiar mi clase factura, y así podríamos seguir...

Qué nos dice esto? que la clase Factura tiene muchos motivos por los cuales podría cambiar. Veamos lo que se dice en el libro "Head First Design Patterns" relacionado con el tema del cambio.

No importa donde estés trabajando, qué estés construyendo o qué lenguaje de programación estés usando, cual es la única y verdadera constante que siempre va a estar con nosotros? El CAMBIO, es decir, no importa qué tan bien diseñes tu aplicación, a medida que pase el tiempo una aplicación deberá crecer y cambiar o inevitablemente morirá.

Parece que es importante que una aplicación esté diseñada para afrontar el cambio...

Imaginemos que GenerarReporte, en la versión inicial de nuestra aplicación, lo único que hacía era generar en formato de texto plano la información contenida dentro de la factura, pero a medida que pasa el tiempo, por requerimientos de nuestro cliente, se decide realizar un cambio y que el reporte sea mas elaborado y además se genere en formato PDF. Esto significaría referenciar desde nuestra clase Factura a librerías de generación de PDF, lo cual hace que nuestra clase se termine acoplando a una librería que sólo es necesaria para generar un reporte y no para calcular descuentos, subtotales, etc...

Si nuestra clase factura la estuviéramos utilizando desde una aplicación que realiza procesamientos en forma desatendida, esto implicaría que nuestra aplicación debería incluir librerías para generar PDF, aún cuando nunca fuera necesario utilizarlas (pensemos que ocurriría si además tuviéramos que pagar una licencia adicional para instalar esta librería en la máquina que corre procesos batch ;)

De haber aplicado SRP en nuestro diseño, quizás este problema se hubiera evitado? Veamos si esto se cumple

Por ahora apliquemos SRP sólo para solucionar este problema particular, mas adelante rediseñaremos otras partes de la clase Factura





De esta manera, la clase Factura puede ser utilizada en forma desacoplada del generador de reportes y, por lo tanto, de la librería de generación de PDF.


public class FacturaPrinter
{
public void ImprimirFactura(Factura factura)
{
string ruta = getRutaGeneracion();

var generadorReporteFactura = new GeneradorReporteFactura();
generadorReporteFactura.GenerarReporte(ruta, factura);

imprimir(ruta);
}

private string getRutaGeneracion()
{
//obtengo la ruta donde se debe generar la factura
}

private void imprimir(string rutaReporte)
{
//imprimo el reporte generado
}
}


Esto fue logrado ya que le hemos quitado a la clase Factura la responsabilidad de saber imprimirse y hemos delegado esa responsabilidad en una nueva clase GeneradorReporteFactura.

SRP nos salvó de tener que pagar licencias adicionales de la librería para generación de PDF!!

Veamos que SRP no solo minimiza el impacto ante los cambios, sino que es útil en otros aspectos.

Testeo unitario

Imaginemos que el método CalcularDescuentos está programado de tal manera que llama internamente al método CalcularSubtotal, y en base a ese valor determina los descuentos a aplicar (por ej, por montos superiores a $1000 se realiza un descuento del 5%). Luego, en forma adicional, también se calculan otros descuentos en base a otras condiciones, como por ejemplo si el cliente es preferencial, si la forma de pago es en efectivo, etc...

Para poder hacer un testeo unitario que sólo verifique el algoritmo de CalcularDescuentos, necesito armar una instancia de factura que contenga lineas con datos como la cantidad, el precio unitario, etc... de forma tal que cuando se llame a CalcularSubtotal, el método me dé un valor X, para luego poder realizar el cálculo de los descuentos. Qué ocurre si además el constructor de líneas de factura necesita que le pasemos una instancia de un Producto, el cual a su vez es complejo de generar? Digamos que cada vez se hace mas complejo realizar un testeo unitario del método CalcularDescuentos...

Apliquemos SRP para intentar solucionar este problema



En este caso, a diferencia del caso anterior, el calculador de descuentos es una dependencia de la factura, de esta manera Factura.CalcularDescuentos delega el cálculo a la clase CalculadorDescuentosFactura.


public class Factura
{
public Factura(CalculadorDescuentosFactura calculadorDescuentosFactura)
{
_calculadorDescuentosFactura = calculadorDescuentosFactura;
}

private CalculadorDescuentosFactura _calculadorDescuentosFactura;

public void CalcularDescuentos()
{
Descuentos = _calculadorDescuentosFactura.CalcularDescuentos(this);
}

// ....
}


Luego, a la hora de hacer un testeo unitario del método CalculadorDescuentosFactura.CalcularDescuentos, me alcanza con pasar como parámetro un mock de la clase Factura (un objeto factura falso) configurado de forma tal, que devuelva un valor X definido por mi cuando el método CalcularSubtotal sea llamado.
NOTA: cabe aclarar que para poder realizar esto, Factura debería ser una abstracción (una interfaz, por ejemplo) y no una clase concreta. De esta forma se podría substituir por un mock de la misma. De esto vamos a hablar cuando tratemos los otros principios SOLID

En este caso SRP nos ayudó a tener un código que es mas fácil de testear.

Claridad del código

Pensemos que existe la remota posibilidad de que alguien necesite modificar la forma en que se calculan los impuestos de una factura y esa persona no es quien originalmente programó la clase... o efectivamente es quien la programó pero lo hizo hace tiempo y ya no se acuerda cómo estaba diseñada la clase. (suele pasar, no? ;)

Esta persona deberá recorrer toda la clase factura, tratando de ubicar en qué parte de la misma está la lógica de cálculo de impuestos. En el ejemplo esta tarea parece bastante fácil, ya que hay un método CalcularImpuestos, pero pensemos qué pasaría si el cálculo de impuestos es más complejo y no fuera tan fácil de identificar esta lógica.

Aplicando SRP podríamos aislar esta lógica en una clase separada, y de esta forma sería mucho mas fácil de entender y seguir el diseño. Este caso es similar al del calculador de descuentos, así que aplicaremos la misma solución.




public class Factura
{
public Factura(CalculadorDescuentosFactura calculadorDescuentosFactura,
CalculadorImpuestosFactura calculadorImpuestosFactura)
{
_calculadorImpuestosFactura = calculadorImpuestosFactura;
_calculadorDescuentosFactura = calculadorDescuentosFactura;
}

private CalculadorDescuentosFactura _calculadorDescuentosFactura;
private CalculadorImpuestosFactura _calculadorImpuestosFactura;

public void CalcularImpuestos()
{
Impuestos = _calculadorImpuestosFactura.CalcularImpuestos(this);
}

// ....
}


SRP es un principio que nos permite tener diseños que respondan bien ante cambios, fáciles de testear y claros, asi que la pregunta que podríamos plantear ahora es, debo aplicar este principio SIEMPRE?

Si llevamos SRP al extremo, terminaríamos con clases que sólo tengan un único método, lo cual tampoco es recomendable, ya que estaríamos incrementando la complejidad de nuestro diseño (pensemos que se incrementa altamente la cantidad de dependencias entre clases y esto no es algo "gratis"). Es por esto que, como todo principio SOLID, siempre es importante tenerlo presente para identificar posibles falencias que pueda tener nuestro diseño, y luego decidir en cada caso puntual si es conveniente aplicarlo o no. (si estamos aprendiendo, ante la duda quizás lo recomendable sea aplicar el principio, luego la experiencia nos dirá...)

Algunos "olores" que nos pueden indicar que conviene aplicar SRP


  • Tengo clases muy extensas que hacen de todo
  • Cuando toco código asociado a una funcionalidad A, se termina rompiendo la funcionalidad B
  • Cuando toco código asociado a una funcionalidad A, tengo que testear las funcionalidades A, B y C porque no sé qué se podría romper
  • Tengo clases con nombres como FacturaManager, FacturaHandler, FacturaAdmin, etc... (si tienen esos nombres muy probablemente es porque no quedaba clara cual era su responsabilidad a la hora de nombrarlas)


Por último dejo algunos links a material de referencia sobre el principio SRP y principios SOLID en general, en el próximo post vamos a estar tratando el principio abierto/cerrado (OCP)

Grupo de estudio sobre principios SOLID del cual participo en la comunidad Alt.net hispano, con la coordinación de Fernando Claverino

SOLID - Wikipedia

Libro Head first object-oriented analysis and design (en el capítulo 8 se tratan varios principios de diseño, incluyendo SRP)

Artículos de Robert C. Martin sobre principios SOLID

Libro Agile principles, patterns, and practices in C# de Robert C. Martin

Espero que sea de utilidad, cualquier comentario será bienvenido.

8 de junio de 2010

Nueva versión de NHTrace

Hay una nueva versión de NHTrace con dos nuevas funcionalidades:

  • Copiar comandos sql formateados para que sean más legibles
  • Copiar comandos y reemplazar automáticamente los parámetros que tuvieran los mismos




"Copy formatted sql" guarda lo siguiente en el portapapeles

INSERT
INTO
"Product"
(Name, UnitPrice, Discontinued, Id)
VALUES
(@p0, @p1, @p2, @p3);
@p0 = 'Apple0', @p1 = 0.25, @p2 = False, @p3 = 1001


"Copy sql and replace parameters" guarda lo siguiente en el portapapeles

INSERT
INTO
"Product"
(Name, UnitPrice, Discontinued, Id)
VALUES
('Apple0', 0.25, False, 1001);


La nueva versión se puede descargar desde esta ubicación

Quiero aprovechar para agradecerle a Pablo ->

quien me sugirió agregar estas funcionalidades a NHTrace

Espero que sea útil! cualquier comentario será bienvenido.