De acuerdo con la respuesta aceptada sobre "¿ Razonamiento para preferir las variables locales a las variables de instancia? ", Las variables deben vivir en el menor alcance posible.
Simplifique el problema en mi interpretación, significa que deberíamos refactorizar este tipo de código:
public class Main {
private A a;
private B b;
public ABResult getResult() {
getA();
getB();
return ABFactory.mix(a, b);
}
private getA() {
a = SomeFactory.getA();
}
private getB() {
b = SomeFactory.getB();
}
}
en algo como esto:
public class Main {
public ABResult getResult() {
A a = getA();
B b = getB();
return ABFactory.mix(a, b);
}
private getA() {
return SomeFactory.getA();
}
private getB() {
return SomeFactory.getB();
}
}
pero de acuerdo con el "espíritu" de "las variables deben vivir en el alcance más pequeño posible", ¿no es "nunca tener variables" tener un alcance más pequeño que "tener variables"? Así que creo que la versión anterior debería ser refactorizada:
public class Main {
public ABResult getResult() {
return ABFactory.mix(getA(), getB());
}
private getA() {
return SomeFactory.getA();
}
private getB() {
return SomeFactory.getB();
}
}
así que eso getResult()
no tiene ninguna variable local en absoluto. ¿Es eso cierto?
refactoring
scope
local-variable
ocomfd
fuente
fuente
final
palabras clave o no.Respuestas:
No. Hay varias razones por las cuales:
Y así.
fuente
var taxIndex = getTaxIndex();
).now()
. Ej .), Eliminar la variable y llamar al método más de una vez puede generar errores. Esto puede crear una situación que es realmente sutil y difícil de depurar. Puede parecer obvio, pero si está en una misión de refactorización ciega para eliminar variables, es fácil terminar presentando fallas.De acuerdo, deben evitarse las variables que no son necesarias y que no mejoran la legibilidad del código. Cuantas más variables estén dentro del alcance en un punto dado en el código, más complejo será entender ese código.
Realmente no veo el beneficio de las variables
a
yb
en su ejemplo, por lo que escribiría la versión sin variables. Por otro lado, la función es tan simple en primer lugar que no creo que importe demasiado.Se vuelve más problemático cuanto más larga sea la función y más variables estén dentro del alcance.
Por ejemplo si tienes
En la parte superior de una función más grande, aumenta la carga mental de comprender el resto del código al introducir tres variables en lugar de una. Usted tiene que leer a través del resto del código para ver si
a
ob
se utilizan de nuevo. Los locales que tienen un alcance mayor de lo que necesitan son malos para la legibilidad general.Por supuesto, en el caso en que es necesaria una variable (por ejemplo, para almacenar un resultado temporal), o cuando una variable que hace mejorar la legibilidad del código, a continuación, se debe tener.
fuente
var result = getResult(...); return result;
es que puede establecer un punto de interrupciónreturn
y aprender quéresult
es exactamente .Además de las otras respuestas, me gustaría señalar algo más. El beneficio de mantener pequeño el alcance de una variable no es solo reducir la cantidad de código que tiene acceso sintácticamente a la variable, sino también la cantidad de posibles rutas de flujo de control que pueden modificar potencialmente una variable (ya sea asignándole un nuevo valor o llamando un método de mutación en el objeto existente contenido en la variable).
Las variables de ámbito de clase (instancia o estática) tienen significativamente más rutas de flujo de control posibles que las variables de ámbito local porque pueden ser mutadas por métodos, que pueden llamarse en cualquier orden, cualquier número de veces y, a menudo, por código fuera de la clase .
Echemos un vistazo a su
getResult
método inicial :Ahora, los nombres
getA
ygetB
pueden sugerir que se asignarán athis.a
ythis.b
, no podemos saberlo con solo mirarlogetResult
. Por lo tanto, es posible que los valoresthis.a
ythis.b
pasados almix
método provengan del estado delthis
objeto anterior al quegetResult
se invocó, lo cual es imposible de predecir ya que los clientes controlan cómo y cuándo se invocan los métodos.En el código revisado con el local
a
y lasb
variables, está claro que hay exactamente un flujo de control (sin excepción) desde la asignación de cada variable hasta su uso, porque las variables se declaran justo antes de ser utilizadas.Por lo tanto, existe un beneficio significativo al mover variables (modificables) del ámbito de clase al ámbito local (así como mover variables (modificables) desde el exterior de un bucle hacia el interior), ya que simplifica el razonamiento del flujo de control.
Por otro lado, eliminar variables como en su último ejemplo tiene menos beneficio, porque realmente no afecta el razonamiento del flujo de control. También pierde los nombres dados a los valores, lo que no sucede cuando simplemente mueve una variable a un ámbito interno. Esta es una compensación que debe tener en cuenta, por lo que eliminar variables podría ser mejor en algunos casos y peor en otros.
Si no desea perder los nombres de las variables, pero aún así quiere reducir el alcance de las variables (en el caso de que se utilicen dentro de una función más grande), puede considerar ajustar las variables y sus usos en una declaración de bloque ( o moviéndolos a su propia función ).
fuente
Esto depende un poco del lenguaje, pero diría que uno de los beneficios menos obvios de la programación funcional es que alienta al programador y lector de código a no necesitarlos. Considerar:
O algo de LINQ:
O Node.js:
La última es una cadena de invocación de una función en el resultado de una función anterior, sin ninguna variable intermedia. Introducirlos lo haría mucho menos claro.
Sin embargo, la diferencia entre el primer ejemplo y los otros dos es el orden implícito de operación . Esto puede no ser el mismo que el orden en que se calcula realmente, pero es el orden en el que el lector debe pensarlo. Para los dos segundos esto se deja de izquierda a derecha. Para el ejemplo de Lisp / Clojure es más como de derecha a izquierda. Debe ser un poco cauteloso al escribir código que no esté en la "dirección predeterminada" para su idioma, y las expresiones "intermedias" que mezclan los dos definitivamente deben evitarse.
El operador de tubería de F #
|>
es útil en parte porque le permite escribir cosas de izquierda a derecha que de otro modo tendrían que ser de derecha a izquierda.fuente
myCollection.Select(_ => _.SomeProp).Where(_ => _.Size > 4);
Yo diría que no, porque debería leer "el alcance más pequeño posible" como "entre los ámbitos existentes o los que son razonables de agregar". De lo contrario, implicaría que debe crear ámbitos artificiales (p. Ej.,
{}
Bloques gratuitos en lenguajes tipo C) solo para asegurarse de que el alcance de una variable no se extienda más allá del último uso previsto, y eso generalmente sería desaprobado como ofuscación / desorden a menos que ya exista Una buena razón para que el alcance exista independientemente.fuente
Considere las funciones ( métodos ). Allí no está dividiendo el código en la subtarea más pequeña posible, ni la pieza de código singleton más grande.
Es un límite cambiante, con delimitación de tareas lógicas, en piezas consumibles.
Lo mismo vale para las variables . Señalando estructuras de datos lógicos, en partes comprensibles. O también simplemente nombrando (estableciendo) los parámetros:
Pero, por supuesto, tener una declaración en la parte superior, y doscientas líneas más, el primer uso se acepta hoy en día como un mal estilo. Esto es claramente lo que pretende decir "las variables deberían vivir en el menor alcance posible" . Como un muy cercano "no reutilice variables".
fuente
Lo que falta es la razón de NO es la depuración / legibilidad. El código debe estar optimizado para eso, y los nombres claros y concisos ayudan mucho, por ejemplo, imagina un 3 vías
esta línea es corta, pero ya es difícil de leer. Agregue algunos parámetros más, y eso si abarca varias líneas.
Me resulta más fácil leer y comunicar significado, por lo que no tengo problemas con las variables intermedias.
Otro ejemplo sería lenguajes como R, donde la última línea es automáticamente el valor de retorno:
esto es peligroso, ¿se espera o se necesita el retorno? esto es más claro:
Como siempre, esta es una decisión: elimine las variables intermedias si no mejoran la lectura; de lo contrario, consérvelas o preséntelas.
Otro punto puede ser la depuración: si los resultados intermedios son de interés, puede ser mejor simplemente introducir un intermediario, como en el ejemplo R anterior. La frecuencia con la que se requiere esto es difícil de imaginar y tenga cuidado con lo que verifica (demasiadas variables de depuración son confusas), nuevamente, una llamada de juicio.
fuente
Refiriéndose solo a su título: absolutamente, si una variable es innecesaria, debe eliminarse.
Pero "innecesario" no significa que se pueda escribir un programa equivalente sin usar la variable, de lo contrario nos dirían que deberíamos escribir todo en binario.
El tipo más común de variable innecesaria es una variable no utilizada, cuanto menor es el alcance de la variable, más fácil es determinar que es innecesaria. Si una variable intermedia es innecesaria es más difícil de determinar, porque no es una situación binaria, es contextual. De hecho, un código fuente idéntico en dos métodos diferentes podría producir una respuesta diferente por parte del mismo usuario, dependiendo de la experiencia pasada en la solución de problemas en el código circundante.
Si su código de ejemplo era exactamente como se representa, sugeriría deshacerse de los dos métodos privados, pero no me preocuparía si guardó el resultado de las llamadas de fábrica en una variable local o simplemente los usó como argumentos para la mezcla método.
La legibilidad del código supera todo excepto el funcionamiento correcto (incluye correctamente criterios de rendimiento aceptables, que rara vez son "lo más rápido posible").
fuente