Sie sind auf Seite 1von 10

Archivo fuente en D:\C#\Codigos Fuentes

http://ltuttini.blogspot.com.ar/2012/09/como-evitar-el-uso-del-switch-22.html

Como evitar el uso del switch (2/2)

Introduccin

Ester artculo ser una expansin y mejora del anterior Como evitar el uso del switch (1/2) Bsicamente se plantearan dos cambios significativos definir una clase base, donde poder definir cdigo repetitivo el combobox resuelva de forma directa la instancia de calculo

Definir una clase base

Una de de los principales problemas que presenta a simple vista es el cdigo similar en cada implementacin de las clases de calculo, en la mayora de los casos el bloque

es idntico, salvando lo resaltado en crculos que varia en cada implementacin, lo resaltado se trata justamente de las funciones de fecha que el plazo determina en cada caso Ahora bien, como se podra evitar repetir cdigo ? Para poder lograrlo realizaremos varias modificaciones, la primer ser cambiar la Interface por una clase abstract, esto nos permitir definir una clase base que puede definir cdigo reutilizable

public abstract class ResolverPlazo { public abstract PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota); protected PazoPagoResult Calcular(DateTime fechainicio, decimal monto,

decimal montocuota, Func<DateTime, int, DateTime> formulafechaFin, Func<DateTime, int, DateTime> formulafechaItem) { // //Se calculan la cantidad de cuotas // int cantcuotas = Convert.ToInt32(Math.Floor(monto / montocuota)); // // Se define la entidad de respuesta // PazoPagoResult result = new PazoPagoResult() { FechaFin = formulafechaFin(fechainicio, cantcuotas) }; // //crea la lista de Pagos // for (int cuota = 1; cantcuotas >= cuota; cuota++) { ItemPago item = new ItemPago() { Fecha = formulafechaItem(fechainicio, cuota), Monto = montocuota * cuota }; result.ListaPagos.Add(item); } return result; } } Hay algunos puntos importantes por resaltar, el primero se define un mtodo como abstract, cada clase concreta ser responsabilidad desarrollarlo, pero adems para ayudar se define un mtodo adicional que actuaria como tmplate para el calculo, define el cdigo que detectamos como repetitivo y aade un punto de extensibilidad por medio del uso de Func<>

En los lugares donde se requiere especificidad que solo la clase concreta conoce se deja el lugar abierto para poder hacerlo, bsicamente el Func<> es un delegado que recibe una fecha y un valor numrico, y espera como respuesta una fecha Las clases concretas se ven ahora beneficiadas, reducindose notablemente el cdigo

public class SinPlazoPago : ResolverPlazo { public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota) { return new PazoPagoResult() { FechaFin = fechainicio, ListaPagos = null }; } } public class UnSoloPago : ResolverPlazo { public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota) { return new PazoPagoResult() { FechaFin = fechainicio, ListaPagos = new List<ItemPago>() { new ItemPago(){ Fecha = fechainicio, Monto = monto } } }; } } public class Semanal : ResolverPlazo { public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota)

{ return base.Calcular(fechainicio, monto, montocuota, (fecha, cuotas) => fecha.AddDays(7 * cuotas), (fecha, cuota) => fecha.AddDays(7 * cuota)); } } public class Quincenal : ResolverPlazo { public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota) { return base.Calcular(fechainicio, monto, montocuota, (fecha, cuotas) => fecha.AddDays(15 * cuotas), (fecha, cuota) => fecha.AddDays(15 * cuota)); } } public class Mensual : ResolverPlazo { public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota) { return base.Calcular(fechainicio, monto, montocuota, (fecha, cuotas) => fecha.AddMonths(1 * cuotas), (fecha, cuota) => fecha.AddMonths(1 * cuota)); } } public class Bimestral : ResolverPlazo { public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota) {

return base.Calcular(fechainicio, monto, montocuota, (fecha, cuotas) => fecha.AddMonths(2 * cuotas), (fecha, cuota) => fecha.AddMonths(2 * cuota)); } } public class Trimestral : ResolverPlazo { public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota) { return base.Calcular(fechainicio, monto, montocuota, (fecha, cuotas) => fecha.AddMonths(3 * cuotas), (fecha, cuota) => fecha.AddMonths(3 * cuota)); } } public class Semestral : ResolverPlazo { public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota) { return base.Calcular(fechainicio, monto, montocuota, (fecha, cuotas) => fecha.AddMonths(6 * cuotas), (fecha, cuota) => fecha.AddMonths(6 * cuota)); } } public class Anual : ResolverPlazo { public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota) { return base.Calcular(fechainicio, monto,

montocuota, (fecha, cuotas) => fecha.AddYears(1 * cuotas), (fecha, cuota) => fecha.AddYears(1 * cuota)); } } Solo las dos primeras clases (que no tienes lgica en la calculo de los plazos de pago) quedaron como se definen originalmente, mientras que el resto hace uso de la funcionalidad definida en la clase base, ntese como las expresiones lambda en los parmetros del delegado permite variar el calculo de la fecha en cada caso.

Resolver de forma directa la clase de calculo

Este caso quizs no aplique en todas los casos ya que se requiere de entorno en donde los controles puedan conservar las instancias de los tems con que con cargados, en este caso por tratarse de un ejemplo con Windows Application puede aplicarse, pero si seria un entorno web no se podria ya que los controles no conservan estado, se debera seguir usando la implementacin original. Lo que se pretende es poder quitar el mtodo que usa el Dictionary<> para definir que clase concreta resolver el calculo, el primer cambio se realiza en la clase que se usara para cargar el combo public class Plazo { public int Id { get; set; } public string Desc { get; set; } public ResolverPlazo Instancia { get; set; } } se agrega una propiedad que define la instancia de la clase que resuelve el calculo de plazos para ese tem El siguiente cambio impacta en la forma como se devuelve la lista de tems, se define nueva la propiedad con la instancia concreta de cada implementacin de calculo

public static class PlazosHelper { public static List<Plazo> ObtenerPlazos() { return new List<Plazo>() { new Plazo() { Id=1, Desc="Un solo pago", Instancia = new UnSoloPago() new Plazo() { Id=2, Desc="Semanal", Instancia = new Semanal() new Plazo() { Id=4, Desc="Mensual", Instancia = new Mensual() }, }, }, }, }, }, new Plazo() { Id=3, Desc="Quincenal", Instancia = new Quincenal() new Plazo() { Id=5, Desc="Bimestral", Instancia = new Bimestral() new Plazo() { Id=6, Desc="Trimestral", Instancia = new Trimestral() new Plazo() { Id=7, Desc="Semestral", Instancia = new Semestral() new Plazo() { Id=8, Desc="Anual", Instancia = new Anual() }; } public static List<Plazo> ObtenerPlazosConItemOpcional() { List<Plazo> plazos = ObtenerPlazos(); plazos.Insert(0, new Plazo() { Id = 0, Desc = "<<<Seleccione>>>", Instancia = new SinPlazoPago() }); return plazos; } } Solo queda cambiar la forma como se recupera el tem del combo y se invoca el mtodo de calculo } },

private void Calcular() { errorProv.Clear(); decimal monto = 0; if (!decimal.TryParse(txtMonto.Text, out monto)) { errorProv.SetError(txtMonto, "Debe ingresar un valor numerico");

return; } decimal montoporcuota = 0; if (!decimal.TryParse(txtMontoPorCuota.Text, out montoporcuota)) { errorProv.SetError(txtMontoPorCuota, "Debe ingresar un valor numerico"); return; } // // Recuperamos la instancia de la entidad bindeada al combo // ResolverPlazo calcularPlazo = ((Plazo)cmdPlazo.SelectedItem).Instancia; // // se invoca de forma directa la operacion de calcular // PazoPagoResult result = calcularPlazo.Calcular(dtpFechaInicio.Value, monto, montoporcuota); txtResultado.Text = result.FechaFin.ToShortDateString(); dgvListaPagos.DataSource = result.ListaPagos; } La clave esta en estas dos lneas // // Recuperamos la instancia de la entidad bindeada al combo // ResolverPlazo calcularPlazo = ((Plazo)cmdPlazo.SelectedItem).Instancia; // // se invoca de forma directa la operacion de calcular // PazoPagoResult result = calcularPlazo.Calcular(dtpFechaInicio.Value, monto, montoporcuota);

Como cada tem del combo permite recuperar la clase con que fue creado, se puede castear al tipo concreto para as recuperar la instancia de la clase que posee la lgica de calculo de las cuotas, con eso se evita usa un mtodo adicional que defina un diccionario de correspondencias entre un id y su instancia Ya no se requiere el SelectedValue, porque no se trabaja con ningn id o cdigo, sino que se accede directo a la instancia de la clase concreta, acortando las interacciones