Muchas veces habéis escrito métodos o funciones en las que recibís un parámetro, dos parámetros, tres, cuatro... Y seguro que alguna vez habéis llegado a un punto en el que habéis tenido la sensación de que el número de parámetros se os ha ido de las manos. En este momento, estaremos percibiendo el code smell llamado "Long Parameter List" o "Lista de Parámetros Larga", todo un clásico en el aromático arte de la programación ;)
Porque, indudablemente, cuando un método o función recibe demasiados parámetros puede volverse difícil de leer, entender y mantener, y son muy propensos a errores de codificación, sobre todo si los tipos de datos de los parámetros son similares o si su orden no es claro o intuitivo.
Pero, ¿cuántos parámetros son demasiados parámetros? ¿Cinco? ¿Diez? ¿Veinte? Como suele ocurrir, no existe una respuesta única y categórica sobre el tema, aunque sí existen algunas recomendaciones que pueden ayudarnos a tomar una decisión cuando nos enfrentamos a esta situación.
Los problemas de tener demasiados parámetros
Un método o función que recibe demasiados parámetros se considera generalmente un code smell. No es un error en sí mismo, pero suele indicar que algo no está bien diseñado y que probablemente necesite una revisión o refactorización.
El problema más evidente de tener demasiados parámetros es que automáticamente es más difícil de leer y entender. Cada parámetro adicional añade complejidad y puede hacer que el propósito del método o función sea menos claro, lo que puede llevar a confusiones y errores al utilizarlo. También, el código será más extenso, lo que puede extender el aroma a otros smells como Long Method, Primitive obsession, Feature Envy y más fragancias desagradables.
Además, si los parámetros son de tipos similares o tienen nombres poco descriptivos, es fácil que al invocar la función se ordenen de forma incorrecta, lo que en muchos casos puede provocar errores difíciles de detectar y corregir.
Y por supuesto, puede generar problemas de mantenimiento y escalabilidad del código. Si un método o función tiene muchos parámetros, cualquier cambio en su firma (añadir, eliminar o modificar parámetros) puede requerir cambios en múltiples ubicaciones, lo que aumenta la complejidad y el riesgo de introducir nuevos errores.
Pero también tiene implicaciones en el diseño de nuestro software. Usar demasiados parámetros puede ser un indicativo de que estamos violando el principio de responsabilidad única (SRP, por sus siglas en inglés), lo que significa que está haciendo demasiadas cosas a la vez y debería ser dividido en métodos más pequeños y específicos. Podría indicar igualmente una baja cohesión, porque es posible que los parámetros no estén relacionados entre sí y el ámbito del método sea demasiado amplio.
Pero, ¿cuántos parámetros son demasiados?
Como comentaba algo más arriba, no existe una respuesta única y definitiva a esta pregunta, pero sí hay algunas recomendaciones generales que pueden servirnos de guía.
Robert C. Martin, también conocido como "Uncle Bob", sugiere que el número ideal de parámetros es cero. Está claro, si un método no recibe ningún parámetro, no dependerá de ningún dato externo, por lo que no existirá código de validación, ni transformaciones de datos, ni la lógica dependerá de ellos. En definitiva, el método será más simple y directo y, a lo sumo, podrá operar con datos internos de su objeto o valores constantes; el código será más conciso, fácil de leer y entender. Todo ventajas 😉
Sin embargo, en el mundo real es un objetivo difícil de alcanzar, por lo que en la mayoría de escenarios, que nuestros métodos o funciones no tengan parámetros es más una aspiración teórica que otra cosa.
A partir de ese límite ideal, está claro que conforme va creciendo el número de parámetros, la complejidad del método también aumenta, y con ella la dificultad para entenderlo y mantenerlo. En su clásico libro Clean Code, Martin dice que usar más de tres parámetros debería requerir una justificación bien argumentada.
En general, la comunidad de desarrolladores está más o menos de acuerdo en que el ideal es que un método o función no reciba más de 3 o 4 parámetros. A partir de ahí, el código comienza a ser difícil de leer y mantener y es cuando se empieza a considerar que estamos ante un "code smell" que puede traernos problemas.
Steve McConnell, en su libro "Code Complete", establece que el máximo número de parámetros debería rondar los siete, basándose en el número mágico de la psicología cognitiva, propuesto por George A. Miller en 1956, que sugiere que el número de elementos que una persona puede retener en su memoria a corto plazo es de aproximadamente siete (más o menos dos).
Por tanto, la cosa queda así:
| Número de parámetros | Comentario |
|---|---|
| 0 | Ideal, aunque poco probable |
| 1-3 | Bien, fácil de entender y mantener |
| 4-7 | Sólo en casos justificados |
| 8+ | ❌ Evitar a toda costa |
Pero ojo, porque, como siempre, existen casos que pueden romper estas reglas generales. Un ejemplo clásico podría ser una función como la siguiente:
public int SumNineNumbers(
int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9)
{
return n1 + n2 + n3 + n4 + n5 + n6 + n7 + n8 + n9;
}
Si su misión es realizar una acción concreta que requiere un determinado número de valores de entrada, ¿sería incorrecto definirla con nueve parámetros? ¿O es lo que esperaríamos, dado el nombre de la propia función? En mi opinión, en este caso estaría totalmente justificado; cualquier refactorización para reducir el número de parámetros sería introducir una complejidad innecesaria.
¿Y qué alternativas tenemos?
Cuando ya estamos ante un método o función que define o va a definir demasiados parámetros, es importante detenernos a pensar cómo podemos mejorarlo.
En primer lugar, debemos preguntarnos por qué nuestro código necesita tantos datos de entrada. ¿Estamos haciendo quizás demasiadas cosas en el mismo método? ¿Tiene demasiadas dependencias? Si es así, es posible que estemos violando el principio de responsabilidad única (SRP) y deberíamos considerar dividirlo en varios más pequeños y específicos.
// Refactorizar esto:
public void ProcessAndNotifyOrder(
string orderId, string customerName, DateTime orderDate,
string notificationEmail, string notificationMessage)
{
// Lógica para procesar el pedido
// Lógica para notificar al cliente
}
// A esto:
public void ProcessOrder(string orderId, string customerName, DateTime orderDate)
{
// Lógica para procesar el pedido
}
public void NotifyCustomer(string notificationEmail, string notificationMessage)
{
// Lógica para notificar al cliente
}
Pero si realmente necesitamos recibir todos esos argumentos, existen distintas técnicas que pueden ayudarnos a reducir el número de parámetros y evitar problemas.
Por ejemplo, si estamos recibiendo siempre el resultado de haber usado un método, función o propiedad, quizás esa llamada debería realizarse desde dentro de nuestro propio código, como se muestra a continuación:
// Refactorizar esto:
public void Log(string message, string level, string source, DateTime now)
{
// Lógica para registrar el mensaje
}
// A esto:
public void Log(string message, string level, string source)
{
DateTime now = DateTime.Now; // Obtenemos el timestamp dentro del método
// Lógica para registrar el mensaje
}
También, si nuestro método o función recibe parámetros booleanos, deberíamos considerar revisarlo. Como vimos hace tiempo en el post "¿Usar booleanos? ¿Sí, no, a veces?", este tipo de parámetros suele introducir ambigüedad y confusión, ya que no siempre está claro qué significa cada valor. En su lugar, quizás podríamos usar dos funciones diferentes, cada una con un propósito claro y específico:
// Refactorizar esto:
public void Log(string message, string level, string source, bool isError)
{
// Lógica para registrar el mensaje
}
// A esto:
public void LogInfo(string message, string level, string source)
{
// Lógica para registrar el mensaje
}
public void LogError(string message, string level, string source)
{
// Lógica para registrar el mensaje
}
Otra posibilidad, que seguro habréis visto con cierta frecuencia, es el caso de métodos que reciben argumentos procedentes de un mismo objeto. En estos casos, podríamos considerar recibir el objeto completo en lugar de sus propiedades individuales:
// Refactorizar esto:
public void ProcessOrder(
string orderId, string customerName, string customerEmail, DateTime orderDate)
{
// Lógica para procesar el pedido
}
// A esto:
public void ProcessOrder(Order order)
{
// Lógica para procesar el pedido
}
Otra solución bastante habitual es usar la refactorización conocida como Introduce Parameter Object. Esta técnica consiste en agrupar varios parámetros relacionados en un único objeto, lo que reduce el número de parámetros y mejora la legibilidad del código.
Por ejemplo, podemos ver el resultado de aplicar esta técnica en el siguiente código:
// Refactorizar esto:
public Task<IEnumerable<Customer>> GetCustomersAsync(
int pageNumber, int pageSize, string sortBy, string filter, bool includeInactive)
{
// Lógica para obtener los clientes
}
// A esto:
public Task<IEnumerable<Customer>> GetCustomersAsync(CustomerQuery query)
{
// Lógica para obtener los clientes
}
Aparte, si el objeto de parámetros es muy complejo, puede ser útil combinar este enfoque con el patrón Builder, que permite construirlo paso a paso. Por ejemplo, podríamos tener un código como el siguiente:
// Refactorizar esto:
var query = new CustomerQuery
{
PageNumber = 1,
PageSize = 20,
SortBy = "Name",
IncludeInactive = false,
IncludeOrders = false,
IncludeAddresses = true
};
var customers = await GetCustomersAsync(query);
// A esto:
var query = new CustomerQueryBuilder()
.WithPageNumber(1)
.WithPageSize(20)
.SortedByName()
.IncludeAddresses()
.Build();
var customers = await GetCustomersAsync(query);
¡Espero que os haya resultado interesante!
Publicado en Variable not found.


Aún no hay comentarios, ¡sé el primero!
Enviar un nuevo comentario