¿Debo reutilizar variables?

59

¿Debo reutilizar variables?

Sé que muchas mejores prácticas dicen que no debes hacerlo, sin embargo, más tarde, cuando un desarrollador diferente está depurando el código y tiene 3 variables que se parecen, y la única diferencia es que se crean en diferentes lugares del código, él podría ser confuso. Las pruebas unitarias son un gran ejemplo de esto.

Sin embargo, yo no sé que las mejores prácticas son la mayoría de las veces en contra de ella. Por ejemplo, dicen no "anular" los parámetros del método.

Las mejores prácticas incluso están en contra de anular las variables anteriores (en Java hay un Sonar que avisa cuando asigna nulla la variable, que no necesita hacerlo para llamar al recolector de basura desde Java 6. No siempre puede controlar qué advertencias están desactivadas; la mayoría de las veces el valor predeterminado está activado).

Adaptador IA
fuente
11
¿Desde Java 6? Nunca fue necesario asignar valores nulos a las variables en ninguna versión de Java. Cuando una variable se sale del alcance, libera la referencia a su objeto.
Kevin Panko
35
La reutilización de variables es una de las primeras cosas que usan los ofuscadores de código
Lelouch Lamperouge
77
La reutilización de variables entra en conflicto con la elección de identificadores decentes para sus variables. En los lenguajes modernos, la reutilización variable no tiene ninguna ventaja que supere los beneficios de tener buenos identificadores.
Joren
44
También tenga en cuenta que no toda reutilización es reutilización. Los antiguos programadores de FORTRAN, incluso aquellos que se han derivado a otros idiomas, usan rutinariamente I, J, K, L, M, N (o i, j, k, l, m, n) para todos nuestros bucles DO (para- bucle) contadores. Estamos acostumbrados a ver cosas como SUM = SUM + A (I, K) * B (K, J) o sum + = a [i] [k] * b [k] [j] ;.
John R. Strohm
1
La reutilización de variables podría justificarse al programar microcontroladores con recursos muy limitados (unos pocos kBytes de RAM).
m.Alin

Respuestas:

130

Su problema aparece solo cuando sus métodos son largos y realizan múltiples tareas en una secuencia. Esto hace que el código sea más difícil de entender (y por lo tanto mantener) per se. La reutilización de variables agrega además un elemento adicional de riesgo, lo que hace que el código sea aún más difícil de seguir y más propenso a errores.

La mejor práctica de la OMI es utilizar métodos lo suficientemente cortos que solo hagan una cosa, eliminando todo el problema.

Péter Török
fuente
¿Qué pasa con las pruebas unitarias? por ejemplo, necesito probar si después de ejecutar el método por segunda vez todavía funciona como se esperaba.
IAdapter
20
@IAdapter, el código de prueba de la unidad es un problema diferente, donde los estándares pueden ser más relajados que para el código de producción. Aún así, hacer que sea legible y fácil de mantener es de alta prioridad. También puede (y debe) extraer métodos de sus pruebas unitarias, para mantenerlos cortos y fáciles de leer (y para eliminar la necesidad de reutilizar variables).
Péter Török
3
@IAdapter, para el escenario que describió, no reutilizaría una variable. Lo apropiado sería algo así como result1 = foo(); result2 = foo(); Assert.AreEqual(result1, result2);que no estoy viendo muchos lugares donde necesitarías reutilizar una variable en este caso.
JSB ձոգչ
1
@IAdapter for (int i = 0; i <2; i ++) {result = foo (); afirmarEquals (esperadoResultado, resultado);}
Bill K
2
+1: este es un buen código de olor para cuando hacer métodos más pequeños.
49

La reutilización variable en un método es una señal fuerte de que debe refactorizarla / dividirla.

Entonces, mi respuesta sería que no debería reutilizarlos, porque si lo hace, sería mucho más difícil refactorizarlos más tarde.

Thanos Papathanasiou
fuente
19

Depende.

Algunas variables pueden crearse exactamente con el propósito de mantener un cierto tipo de datos, que pueden cambiar durante la ejecución de una función. Los códigos de retorno vienen a mi mente aquí, por ejemplo:

void my_function() {
    HRESULT errorcode;
    errorcode = ::SomeWindowsApiFunction();
    if (FAILED(errorcode)) { /* handle error */ }
    errorcode = ::AnotherWindowsApiFunction();
    if (FAILED(errorcode)) { /* handle error */ }
}

El nombre de la variable deja muy claro lo que esta variable está destinada a almacenar. Creo que otros casos de uso como este son posibles, donde una variable es conceptualmente un contenedor que es lógicamente utilizado por diferentes instancias de cosas muy similares durante el curso de una función.

En general, sin embargo, esto debe hacerse solo en circunstancias en que sea absolutamente claro para los posibles lectores del código. En ningún caso, excepto tal vez una optimización extrema sin tener en cuenta la legibilidad del código, si una variable se reutiliza solo porque el tipo se ajusta.

Todo esto básicamente se deriva de las buenas prácticas en nombres variables. Los nombres deben hablar por sí mismos. Si es difícil poner el propósito exacto de todas las reutilizaciones en un nombre corto, lo mejor es usar variables distintas.

Felix Dombek
fuente
15

La razón principal por la que no reutilizo variables (especialmente en pruebas unitarias) es porque introduce una ruta de código innecesaria, una que es difícil de probar y depurar. Una buena prueba unitaria debe ser independiente de otras pruebas y cuando reutiliza la variable de nivel de clase (instancia) en un dispositivo de prueba unitaria, debe asegurarse de afirmar su estado antes de cada prueba. Una buena prueba unitaria también aísla los defectos, por lo que, en teoría, cada caso de prueba (método) solo debe afirmar 1 comportamiento para el sistema bajo prueba. Si sus métodos de prueba están escritos de esta manera, rara vez es necesario o beneficios reutilizar una variable de nivel de método. Por último, en los lenguajes que admiten cierres y procesamiento asincrónico, es realmente difícil razonar qué demonios está sucediendo si está reutilizando variables a lo largo de un método.

Sbrenton
fuente
Entonces, ¿debo escribir métodos de prueba como -testSomething y afirmar para la invocación de un solo método -testSomethingTwoInvocations y afirma solo una segunda invocación?
IAdapter
2
Si. Le ayuda a determinar si es la primera o segunda iteración la que falló. mspec puede ayudar para esto, su árbol de herencia será muy útil.
Bryan Boettcher
Si usa "variable" cuando quiere decir "variable de clase" o "variable de instancia", entonces necesita expresarse más claramente.
gnasher729
13

Deberías usar diferentes variables. Si le preocupa que su colega se confunda, dele nombres que cubran claramente sus roles.
Reutilizar variables es una fuente probable de confusión en el futuro; mejor aclararlo ahora.
En algunos casos, se puede reutilizar el mismo nombre de variable; por ejemplo ien un simple ciclo de conteo. En estos casos, por supuesto, debe asegurarse de que las variables estén dentro de su propio alcance.

EDITAR: Reutilizar variables es a veces una señal de que se viola el Principio de Responsabilidad Única . Verifique si la variable reutilizada se usa en el mismo rol. Si es así, es posible que no se reutilice en absoluto (aunque aún puede ser preferible tener dos variables diferentes, para restringir el alcance de dichas variables). Si se usa en diferentes roles, tiene una violación de SRP en sus manos.

SL Barth - Restablece a Monica
fuente
4

Hay una situación en la que es posible que desee reutilizar una variable independientemente de los excelentes consejos dados por otras respuestas: cuando su compilador necesita una mano amiga.

En algunos casos, su compilador puede no ser lo suficientemente inteligente como para darse cuenta de que una determinada variable asignada por el registro ya no se usa en la siguiente parte del código. Por lo tanto, no reutilizará ese registro teóricamente libre para las siguientes variables, y el código generado puede ser subóptimo.

Tenga en cuenta que no conozco ningún compilador convencional actual que no pueda captar esta situación correctamente, así que nunca, nunca, haga esto a menos que sepa de hecho que su compilador está generando un código subóptimo. Si está compilando para sistemas integrados especiales con compiladores personalizados, es posible que aún se encuentre con este problema.

Joris Timmermans
fuente
17
-1 a menos que pueda señalar una situación del mundo real en la que esto realmente suceda, y donde la reutilización de una variable haga una diferencia apreciable. Esta es una solución teórica a un problema teórico, y no es muy buena. Donde el compilador decide poner variables es la preocupación del compilador; si su compilador genera un código deficiente y si realmente marca la diferencia, repare o reemplace el compilador.
Caleb
3
@Caleb: no siempre tiene acceso al código fuente del compilador para la plataforma en la que está desarrollando, especialmente para las plataformas integradas patentadas. También DID digo en mi respuesta que yo no conozco ningún compiladores corriente actual (o intérpretes / VM de hecho) que no realizan correctamente este análisis liveness en todos los casos que he mirado. Sin embargo, yo he trabajado en sistemas embebidos con los compiladores de propiedad personalizados que eran muy escueto en el pasado (hace 10 años más o menos). El sistema tenía una capacidad muy limitada, al igual que el compilador, por lo que esta situación sí ocurrió allí.
Joris Timmermans
2
+1 Hice exactamente tal reutilización en proyectos anteriores (código de medios altamente optimizado escrito en ANSI C). Hasta donde recuerdo, la diferencia era fácilmente visible en el ensamblaje y medible en los puntos de referencia. Nunca lo hice y espero que nunca tenga que hacer tal reutilización en Java.
mosquito
@Caleb arreglar o reemplazar el compilador puede no ser una opción por varias razones, y simplemente tienes que conformarte con lo que tienes.
@ ThorbjørnRavnAndersen ¿Es concebible que uno se vea obligado a usar un mal compilador y, además, que el rendimiento sea tan crítico que necesite adivinar el compilador y manipular su código para reutilizar un registro? Si. ¿Es probable ? No. MadKeithV está de acuerdo en que no puede proporcionar un ejemplo del mundo real. ¿Es una mejor práctica ? ¿Es buen estilo ? ¿Es un indicador de la calidad del código ? Definitivamente no. ¿Es esta una respuesta útil a la pregunta tal como está escrita? Debido respeto a MadKeithV, pero no creo que lo sea.
Caleb
2

Yo diría que no.

Piense en este escenario: su programa se bloqueó y necesita averiguar qué sucedió inspeccionando un volcado de núcleo ... ¿puede ver la diferencia? ;-)

Fortran
fuente
Si su volcado de núcleo es de una compilación optimizada, puede hacer que las variables compartan memoria de todos modos. Eso es menos útil de lo que podría ser.
Winston Ewert
¡por supuesto, una compilación optimizada no es muy útil para la depuración! pero en cualquier caso todavía tiene la opción de usar una compilación de depuración ... Y cuando tiene el depurador adjunto, no necesita ir paso a paso desde el comienzo de una función para ver cómo cambian las variables, porque no lo hagas! :-D
fortran
2

No, no está mejorando el código que está ejecutando la máquina (... el código de ensamblaje). Deje reutilizando cualquier memoria que el compilador use para la variable en el compilador. Muy a menudo será un registro, y la reutilización no le compró nada. Concéntrese en hacer que el código sea más fácil de leer.

Rawbert
fuente
0

Depende. En un lenguaje dinámico, donde el tipo reside en los valores en lugar de las variables, si tuviera un argumento bien nombrado para una función que, por ejemplo, era una cadena. Si un cambio en el algoritmo significaba que siempre se usaba después de ser interpretado como un entero, entonces, como primer borrador, podría hacer el equivalente de

scrote = int (scrote)

Pero eventualmente buscaría cambiar el tipo enviado a la función y notar el cambio en el tipo de parámetro.

Paddy3118
fuente
1
Poner una línea como "scrote = int (scrote)" en medio de un largo lote de código es una excelente manera de volver locos a los programadores de mantenimiento.
jhocking
Es más probable que el problema esté relacionado con el "lote largo de código" que usted menciona.
Paddy3118
Además de las notas de respuesta aceptadas, la reutilización de variables solo es realmente un problema con largos lotes de código. Básicamente, en cualquier situación en la que escribiría algo como "scrote = int (scrote)", preferiría poner int (scrote) en una nueva variable, o mejor aún, pasar int (scrote) a una nueva función.
jhocking
0

Primero, observe su entorno de desarrollo. Si tiene problemas para depurar porque tiene cinco variables en diferentes ámbitos con nombres idénticos, y el depurador no le muestra cuál de estas variables es la que necesita, entonces no use cinco variables con el mismo nombre. Hay dos formas de lograr esto: use una variable o use cinco nombres diferentes.

Su depurador también puede hacer que sea doloroso depurar una función con muchas variables. Si una función con 20 variables es más difícil de depurar que una con 16 variables, entonces podría considerar reemplazar 5 variables con una. ("Podría considerar" no es lo mismo que "debería siempre").

Está bien tener una variable utilizada en varios lugares siempre que la variable siempre tenga el mismo propósito. Por ejemplo, si diez llamadas de función devuelven un código de error, que se maneja inmediatamente para cada llamada, use una variable y no 10. Pero no use la misma variable para cosas completamente diferentes. Como usar "nombre" para el nombre de un cliente y 10 líneas más tarde usando la misma variable para el nombre de una empresa, eso es malo y lo meterá en problemas. Peor aún, usar "customerName" para el nombre de un cliente y 10 líneas más tarde usando la misma variable "customerName" para el nombre de una empresa.

Es importante destacar que nada es una regla de hierro. Todo tiene ventajas y desventajas. Si las "mejores prácticas" sugieren una cosa, y tiene razones para decir que es una mala idea en su situación específica, entonces no lo haga.

gnasher729
fuente
0

Primero, observe su entorno de desarrollo. Si tiene problemas para depurar porque tiene cinco variables en diferentes ámbitos con nombres idénticos, y el depurador no le muestra cuál de estas variables es la que necesita, entonces no use cinco variables con el mismo nombre. Hay dos formas de lograr esto: use una variable o use cinco nombres diferentes.

Su depurador también puede hacer que sea doloroso depurar una función con muchas variables. Si una función con 20 variables es más difícil de depurar que una con 16 variables, entonces podría considerar reemplazar 5 variables con una. ("Podría considerar" no es lo mismo que "debería siempre").

Está bien tener una variable utilizada en varios lugares siempre que la variable siempre tenga el mismo propósito. Por ejemplo, si diez llamadas de función devuelven un código de error, que se maneja inmediatamente para cada llamada, use una variable y no 10. Hay un problema con esto: por lo general, el compilador le dirá cuando use una variable no inicializada. Pero aquí, si lee el código de error después de la llamada 6, pero la variable no ha cambiado realmente después de la llamada 5, obtendrá datos inútiles sin que el compilador le advierta. Porque la variable se ha inicializado, con datos que ahora son inútiles.

Es importante destacar que nada es una regla de hierro. Todo tiene ventajas y desventajas. Si las "mejores prácticas" sugieren una cosa, y tiene razones para decir que es una mala idea en su situación específica, entonces no lo haga.

gnasher729
fuente
-2

Use variables globales cuando necesite mantener un estado o almacenar constantes. Al reutilizar variables, solo está reutilizando el nombre que apunta a una ubicación en la memoria. Los nombres descriptivos en las inicializaciones solo pueden ganar claridad (suponiendo Java y sin OCD primitivo).

A menos que esté en la ofuscación del código a mano o irrita a otros desarrolladores.

JunoA
fuente