Para resumir hemos visto nombres pobres y firmas pobres. Ahora, otro code smell muy famoso. Cuando tenemos una larga lista de parámetros en nuestras métodos hace que sea difícil de comprender, inclusive, si los parámetros no dicen cuál es su intención.
Veamos los ejemplos:
CheckNotifications(null, 1, true, false, DateTime.Now);
El código anterior nos lleva a una pregunta, ¿Que representan todos estos parámetros? No solamente es dificultoso de entender, también, es difícil de usar.
Veamos cómo refactorizar este tipo de métodos con exceso de parámetros.
public class LongParameterList{ public IEnumerable<FlyTicket> GetFlyTicket( DateTime dateFrom, DateTime dateTo, User user, int locationId, LocationType locationType, int? customerId = null) { if (dateFrom >= DateTime.Now) throw new ArgumentNullException("dateFrom"); if (dateTo <= DateTime.Now) throw new ArgumentNullException("dateTo"); throw new NotImplementedException(); } public IEnumerable<FlyTicket> GetUpcomingFlyTicket( DateTime dateFrom, DateTime dateTo, User user, int locationId, LocationType locationType) { if (dateFrom >= DateTime.Now) throw new ArgumentNullException("dateFrom"); if (dateTo <= DateTime.Now) throw new ArgumentNullException("dateTo"); throw new NotImplementedException(); } private static Tuple<DateTime, DateTime> GetFlyTicketDateRange(DateTime dateFrom, DateTime dateTo, FlyTicketDefinition sd) { if (dateFrom >= DateTime.Now) throw new ArgumentNullException("dateFrom"); if (dateTo <= DateTime.Now) throw new ArgumentNullException("dateTo"); throw new NotImplementedException(); } public void CreateFlyTicket(DateTime dateFrom, DateTime dateTo, int locationId) { if (dateFrom >= DateTime.Now) throw new ArgumentNullException("dateFrom"); if (dateTo <= DateTime.Now) throw new ArgumentNullException("dateTo"); throw new NotImplementedException(); } internal class FlyTicketDefinition { } public class LocationType { } public class User { public object Id { get; set; } } public class FlyTicket { } }
Lo primero que podemos ver a simple vista es que todos los métodos reciben 2 parámetros, dateFrom y dateTo esto quiere decir que están muy relacionados. ¿Que deberíamos hacer? debemos encapsular creando una clase llamada DateRange que nos permitirá reducir de gran manera la cantidad de parámetros. Empecemos con esto:
public class DateRange{ public DateTime _dateFrom { get; set; } public DateTime _dateTo { get; set; } public DateRange(DateTime dateFrom, DateTime dateTo) { _dateFrom = dateFrom; _dateTo = dateTo; } public DateTime DateFrom{ get { return _dateFrom; } } public DateTime DateTo{ get { return _dateTo; } } }
Una vez que tenemos la nueva clase debemos cambiar todas las firmas de nuestros métodos. Recuerda, aquí lo estamos haciendo manualmente pero no estaría mal usar ReSharper para esto. Resharper puede hacer todos los ajustes necesarios automáticamente. Miremos parte de la modificación (Debemos cambiarlo en los 4 métodos):
public IEnumerable<FlyTicket> GetFlyTicket( DateRange dateRange, User user, int locationId, LocationType locationType, int? customerId = null) { if (dateRange.DateFrom >= DateTime.Now) throw new ArgumentNullException("dateFrom"); if (dateRange.DateTo <= DateTime.Now) throw new ArgumentNullException("dateTo"); throw new NotImplementedException(); }
Una vez que lo hayamos modificado en nuestros 4 métodos veremos que todo es más claro y limpio. Pero, tenemos más cosas para ajustar. Estos métodos tienen más cosas en común, DateRange, User, locationId y locationType. Si pensamos, esto parece ser un filtro para el método. Entonces, ¿vamos a repetir los mismos pasos? Para solucionar esto extraemos los parámetros en una clase y la llamaremos FlyTicketQuery.
public class FlyTicketQuery{ private DateRange _dateRange; private User _user; private int _locationId; private LocationType _locationType; private int? _customerId; public FlyTicketQuery(DateRange dateRange, User user, int locationId, LocationType locationType, int? _customerId) { _dateRange = dateRange; _user = user; _locationId = locationId; _locationType = locationType; _customerId = _customerId; } public DateRange DateRange { get { return _dateRange; } } public User User { get { return _user; } } public int LocationId { get { return _locationId; } } public LocationType LocationType { get { return _locationType; } } public int? CustomerId { get { return _customerId; } } }
El resultado final de nuestro método LongParameterList() será el siguiente:
public class LongParameterList{ public IEnumerable<FlyTicket> GetFlyTicket(FlyTicketQuery flyTicketQuery) { if (flyTicketQuery.DateRange.DateFrom >= DateTime.Now) throw new ArgumentNullException("dateFrom"); if (flyTicketQuery.DateRange.DateTo <= DateTime.Now) throw new ArgumentNullException("dateTo"); throw new NotImplementedException(); } }
En los otros 3 métodos seguiremos el camino anterior nuevamente: crearemos una nueva clase, refactorizaremos por la primer clase ReservationQuey y por último eliminaremos la última creada.
Conclusión
Tener más de 3 parámetros la mayoría de tiempo nos indica code-smell. Para estos casos el mejor camino es encapsular los parámetros dentro de una clase. No olvides que podemos usar herramientas como Resharper para facilitar nuestro trabajo.
En próximo post veremos cómo trabajar con parámetro de salida y evitarnos muchos inconvenientes.