Digamos que tengo una serie de corredores con los que necesito encontrar el corredor más alto, el corredor más rápido y el corredor más ligero. Parece que la solución más legible sería:
runners = getRunners();
tallestRunner = getTallestRunner(runners);
fastestRunner = getFastestRunner(runners);
lightestRunner = getLightestRunner(runners);
... donde cada función itera sobre los corredores y realiza un seguimiento de la altura más grande, la mayor velocidad y el peso más bajo. Sin embargo, iterar sobre la matriz tres veces no parece una muy buena idea. En cambio, sería mejor hacer:
int greatestHeght, greatestSpeed, leastWeight;
Runner tallestRunner, fastestRunner, lightestRunner;
for(runner in runners){
if(runner.height > greatestHeight) { greatestHeight = runner.height; tallestRunner = runner; }
if(runner.speed > ...
}
Si bien esto no es demasiado ilegible, puede ser complicado cuando hay más lógica para cada pieza de información que se extrae en la iteración.
¿Cuál es el término medio aquí? ¿Cómo puedo usar solo una iteración mientras mantengo el código dividido en unidades lógicas?
design
code-quality
language-agnostic
readability
mowwwalker
fuente
fuente
Respuestas:
Taskinoor tiene la idea correcta, pero hay una mejor manera de implementarla ... siempre que su idioma admita pasar una referencia de función.
Por ejemplo, así es como lo haría en estilo C #:
Esto es trivial de expandir: en lugar de definir tres funciones, puede definir una matriz de
Func<Runner, Runner, Runner>
funciones anónimas y simplemente ejecutarlas todas. Incluso puede hacerlo con funciones regulares comoRunner pickTallest(Runner x, Runner y)
, aunque luego debe definirlas explícitamente. Sin embargo, la clave es que no tiene que rastrear realmente el valor de cada estadística: solo necesita saber cómo comparar dosRunners
y elegir el que tenga el mejor valor.fuente
Esta es una de esas cosas en las que a menudo desea operar con una gran cantidad de datos, en lugar del principio OO de "una sola pieza de datos".
Así que envolvería toda la lista en una clase que en la creación, analiza la lista y calcula lo que quieres. También usaría esta clase para insertar y eliminar de la lista, de modo que la información empaquetada esté siempre actualizada.
Eso es. ahora tiene un bloque lógico autónomo que responde sus preguntas por usted con solo una iteración en la creación y cuando elimina objetos.
fuente
Puede devolver los tres valores de una vez. En pseudocódigo cerca de Scala:
Eso te dará una tupla de los corredores que estás buscando. Puedes hacer lo mismo en otros idiomas. Puede que tenga que tomar una biblioteca como Guava o Underscore para hacerlo, y puede que tenga que envolver la tupla en un objeto.
fuente
Cree una clase RunnerStats que calcule las estadísticas en un solo
for loop
, tal como lo hace en su segundo fragmento de código.Luego lees las estadísticas en las variables a través de
getters
. Los captadores solo devuelven los valores ya calculados, no calculan nada.De esa manera, obtienes lo mejor de ambas soluciones: eficiencia y legibilidad.
fuente
El problema que está describiendo es muy común: tiene un ciclo que produce ciertos datos y desea agregar múltiples "estadísticas" de los datos generales. La implementación sencilla es, como dijiste, tener múltiples variables locales que se actualizan en cada iteración:
Esto no tiene una buena separación de preocupaciones, porque tiene la lógica de agregación en línea con la producción de datos. Extraer la lógica de agregación en un método (como se propone en esta respuesta ) es una mejora, pero el aislamiento aún no es bueno: los resultados intermedios y (si es necesario) las variables auxiliares como
greatestHeight
todavía tienen que ser variables locales.Entonces, en mi humilde opinión, la única buena solución es extraer tanto la lógica de agregación como las asignaciones en un método.
¿Cómo se puede hacer esto? Primero refactorizando las variables locales a los campos. Luego, por ejemplo, puede extraer un método
updateStats(runner)
que actualice los campostallestRunner
/fastestRunner
/ ... y los camposgreatestHeight
/greatestSpeed
/ ... correspondientes .¿Pero esto no empeora el aislamiento? Sí, al principio, pero esto se puede solucionar mediante una refactorización de clase de extracto : mueva los campos de estadísticas y el
updateStats
método a una nueva clase (por ejemplo, anidada). Entonces, al final, tendrá la siguiente solución legible de un solo paso:fuente