Generalizar el uso variable dentro del código

11

Me gustaría saber si es una buena práctica generalizar variables (use una sola variable para almacenar todos los valores).
Considere un ejemplo simple

 Strings querycre,queryins,queryup,querydel; 
    querycre = 'Create table XYZ ...';
    execute querycre ;
    queryins = 'Insert into XYZ ...';
    execute queryins ;
    queryup  = 'Update  XYZ set ...';
    execute queryup;
    querydel = 'Delete from XYZ ...';
    execute querydel ;

y

 Strings query; 
    query= 'Create table XYZ ... ';
    execute query ;
    query= 'Insert into XYZ ...';
    execute query ;
    query= 'Update  XYZ set ...';
    execute query ;
    query= 'Delete from XYZ ...';
    execute query ;

En el primer caso, uso 4 cadenas, cada una de las cuales almacena datos para realizar las acciones mencionadas en sus sufijos.
En el segundo caso solo 1 variable para almacenar todo tipo de datos.
Tener diferentes variables hace que sea más fácil para otra persona leerlo y comprenderlo mejor. Pero tener demasiados de ellos hace que sea difícil de manejar.

¿Tener demasiadas variables también obstaculiza mi rendimiento?

PD: por favor, no responda wrt el código, por ejemplo, fue solo para transmitir lo que realmente quiero decir.

Shirish11
fuente
Por supuesto, reutilizas la misma variable ... porque la has definido en una función. Para eso están las funciones.
zzzzBov

Respuestas:

26

Tener que hacerte esta pregunta es un olor bastante fuerte que no estás siguiendo SECO (No te repitas). Supongamos que tiene esto, en un hipotético lenguaje de llaves:

function doFoo() {
    query = "SELECT a, b, c FROM foobar WHERE baz = 23";
    result = runQuery(query);
    print(result);

    query = "SELECT foo, bar FROM quux WHERE x IS NULL";
    result = runQuery(query);
    print(result);

    query = "SELECT a.foo, b.bar FROM quux a INNER JOIN quuux b ON b.quux_id = a.id ORDER BY date_added LIMIT 10";
    result = runQuery(query);
    print(result);
}

Refactorizar eso en:

function runAndPrint(query) {
    result = runQuery(query);
    print(result);
}

function doFoo() {
    runAndPrint("SELECT a, b, c FROM foobar WHERE baz = 23");
    runAndPrint("SELECT foo, bar FROM quux WHERE x IS NULL");
    runAndPrint("SELECT a.foo, b.bar FROM quux a INNER JOIN quuux b ON b.quux_id = a.id ORDER BY date_added LIMIT 10");
}

Observe cómo desaparece la necesidad de decidir si usar o no diferentes variables, y cómo ahora puede cambiar la lógica para ejecutar una consulta e imprimir el resultado en un lugar, en lugar de tener que aplicar la misma modificación tres veces. (Por ejemplo, puede decidir que desea bombear el resultado de la consulta a través de un sistema de plantillas en lugar de imprimirlo de inmediato).

tdammers
fuente
2
Me encanta el principio DRY :)
artjom
1
@tdammers, ¿es bueno tener solo 2 líneas de código dentro de una función? considere si tengo esta función doFoo () {print (runQuery ("Selct a, b, c from XYZ"));}
Shirish11
1
No, la pila de llamadas no aumenta: cada llamada a runAndPrintempuja un marco de pila cuando lo llama, y ​​luego lo vuelve a abrir cuando la función sale. Si lo llama tres veces, hará tres pares push / pop, pero la pila nunca crece más de un cuadro a la vez. Realmente solo debe preocuparse por la profundidad de la pila de llamadas con funciones recursivas.
tdammers
3
Y las funciones con solo dos líneas de código están perfectamente bien: si dos líneas forman una unidad lógica, entonces dos líneas lo son. He escrito muchas funciones de una sola línea, solo para mantener un poco de información aislada y en un solo lugar.
tdammers
1
@JamesAnderson: es un ejemplo un tanto artificial, pero sirve para ilustrar un punto. No se trata de cuántas líneas de código tienes. Es cuántas veces declaras el mismo hecho. Eso es lo seco es cerca, así como la única fuente de principio de la verdad, la Shalt de mil no copiar y pegar regla, etc.
tdammers
14

Normalmente, esta es una mala práctica.

Reutilizar una variable de esta manera puede hacer que el código que es confuso para leer y comprender.

Quienes lean el código no esperarán que una variable se reutilice de esa manera y no sabrán por qué un valor establecido al inicio tiene un valor diferente al final de la función.

Los ejemplos que publicó son muy simples y realmente no sufren este problema, pero no son representativos de algún código que reutiliza variables (donde se establece al principio, se reutiliza en algún lugar en el medio, fuera de la vista).

Los ejemplos que ha dado se prestan a la encapsulación en funciones, donde pasaría la consulta y la ejecutaría.

Oded
fuente
¿Qué pasa con el rendimiento del sistema se ve afectado por él?
Shirish11
@ Shirish11 - Podría ser. Depende del compilador, el idioma, el entorno y otras variables.
Oded
Por lo general, los compiladores son buenos para optimizar esto. Sin embargo, siempre depende del compilador / plataforma / caso específico / configuración.
deadalnix
7

El código auto documentado es más fácil de leer y mantener

Siga el Principio de Menos asombro y el precepto del código como documentación : use una variable para un objetivo, para que su uso sea fácil de entender y el código fácil de leer sin explicaciones.

El código correctamente estructurado es más fácil (por lo tanto, más barato) para (re) usar

Además, aquí parece que querysiempre se usa para preparar una declaración antes de ejecutarla. Probablemente sea una señal de que desea refactorizar parte de este código en uno (o más) métodos auxiliares para preparar y ejecutar la consulta (para cumplir con el principio DRY ).

De esta manera, efectivamente:

  • use solo una variable en su método auxiliar para identificar la consulta del contexto actual,
  • necesita escribir menos código cada vez que quiera volver a ejecutar una consulta,
  • haga que su código sea más legible para otros.

Ejemplos:

Considere esto, tomado de su ejemplo, donde la versión refactorizada es obviamente mejor. Por supuesto, su fragmento fue solo un ejemplo para el propósito de esta pregunta, pero el concepto aún es cierto y escalas.

Tu ejemplo 1:

Strings querycre,queryins,queryup,querydel; 
    querycre = 'Create table XYZ ...';
    execute querycre ;
    queryins = 'Insert into XYZ ...';
    execute queryins ;
    queryup  = 'Update  XYZ set ...';
    execute queryup;
    querydel = 'Delete from XYZ ...';
    execute querydel ;

Tu ejemplo 2:

 Strings query; 
    query= 'Create table XYZ ...';
    execute query ;
    query= 'Insert into XYZ ...';
    execute query ;
    query= 'Update  XYZ set ...';
    execute query ;
    query= 'Delete from XYZ ...';
    execute query ;

Ejemplo 3 (pseudocódigo refactorizado):

def executeQuery(query, parameters...)
    statement = prepareStatement(query, parameters);
    execute statement;
end

// call point:
executeQuery('Create table XYZ ... ');
executeQuery('Insert into XYZ ...');
executeQuery('Update  XYZ set ...');
executeQuery('Delete from XYZ ...');

El beneficio se muestra con la reutilización regular.

Anécdota personal

Originalmente comencé como programador en C trabajando con espacio limitado en pantalla, por lo que reutilizar variables tenía sentido tanto para el código compilado (en ese entonces) como para permitir que más código sea legible a la vez.

Sin embargo, después de pasar a los lenguajes de nivel superior y repasar la programación funcional, me acostumbré a usar variables inmutables y referencias inmutables siempre que sea posible para limitar los efectos secundarios.

¿Qué hay para mi ahí dentro?

Si tiene la costumbre de que todas las entradas de su función sean inmutables y devuelva un nuevo resultado (como lo haría una verdadera función matemática), se acostumbra a no duplicar las tiendas.

Por extensión, esto lleva a:

  • escribes funciones cortas,
  • con objetivos bien definidos,
  • que son más fáciles de entender
  • reutilizar,
  • extender (ya sea por herencia OO o por encadenamiento funcional),
  • y documento (como ya autodocumentado).

No estoy diciendo que no haya ningún beneficio para el estado mutable aquí, solo estoy señalando cómo el hábito podría crecer en usted y cómo afecta la legibilidad del código.

haylem
fuente
2

En términos de diseño de código

En general, está bien reutilizar variables para almacenar diferentes valores, después de todo, es por eso que se llaman variables, porque el valor almacenado en ellas varía, siempre que el valor no solo sea del mismo tipo, sino que también signifique lo mismo . Por ejemplo, por supuesto, está bien reutilizar la currentQueryvariable aquí:

for currentQuery in queries:
    execute query;

Naturalmente, hay un ciclo, por lo que debe reutilizar una variable, pero incluso si no hubiera un ciclo, habría estado bien. Si el valor no significa lo mismo, use una variable separada.

Sin embargo, específicamente, el código que está describiendo no se ve muy bien, se repite . Es mucho mejor usar un ciclo o llamadas de método auxiliar (o ambas). Personalmente, rara vez he visto un código de producción que se parezca a tu primera o segunda versión, pero en los casos que tengo, creo que la segunda versión (reutilización variable) fue más común.

En términos de rendimiento

Depende del idioma, los compiladores y los sistemas de tiempo de ejecución utilizados, pero en general no debería haber ninguna diferencia ; en particular, los compiladores para máquinas de registro basadas en pila (como el popular x86 / x86-64) de todos modos solo utilice cualquier memoria de pila libre o regístrese como objetivo de asignación, ignorando por completo si deseaba la misma variable o no.

Por ejemplo, gcc -O2genera exactamente el mismo binario, y la única diferencia de rendimiento que conozco es el tamaño de la tabla de símbolos durante la compilación, completamente insignificante a menos que regrese en el tiempo a los años 60.

Un compilador de Java generará un código de bytes que necesita más almacenamiento para la primera versión, pero el jitter de JVM lo eliminará de todos modos, así que nuevamente, sospecho que prácticamente no habrá un impacto notable en el rendimiento incluso si necesita un código altamente optimizado.

Roble
fuente
0

Creo que reutilizar la variable está bien la mayor parte del tiempo.

Para mí, simplemente reutilizo la variable de consulta la mayor parte del tiempo. Casi siempre ejecuto la consulta justo después. Cuando no ejecuto la consulta de inmediato, generalmente uso un nombre de variable diferente.

Echo dice reinstalar a Mónica
fuente
-1

Puede aumentar el uso de la pila si su compilador es particularmente tonto. Personalmente, no creo que tener una variable separada para cada consulta se agregue a la legibilidad, todavía necesita mirar la cadena de consulta para ver qué hace.

James
fuente
Acabo de proporcionar un ejemplo simple para que sea más fácil para los lectores entender lo que busco. Mi código es mucho más complejo que esto.
Shirish11
-2

En el ejemplo, iría con el segundo ejemplo. Es bastante claro tanto para el lector como para los optimizadores lo que está haciendo. El primer ejemplo es un poco más apropiado, y con un código algo más complicado lo usaría, pero lo hago como:

{
    String query = 'Create table XYZ ...';
    execute query;
}
{
    String query = 'Insert table XYZ ...';
    execute query;
}
And so on...

(En este punto, podría considerar la solución de tdammers ).

El problema con el primer ejemplo es que querycreestá dentro del alcance de todo el bloque, que podría ser extenso. Esto puede confundir a alguien que lee el código. También puede confundir a los optimizadores, lo que podría dejar una escritura innecesaria en la memoria, por lo que querycreestará disponible más adelante si es necesario (lo cual no es así). Con todas las llaves, queryse almacena solo en un registro, si es así.

Con frases como "Crear tabla" y "ejecutar", no me parece que se note una escritura de memoria adicional aquí, por lo que solo criticaría el código por confundir al lector. Pero es útil tener en cuenta esto si está escribiendo código donde la velocidad importa.

RalphChapin
fuente
Estoy en desacuerdo. Si prefiere el segundo ejemplo para mayor claridad, debe refactorizarse a llamadas sucesivas a un método auxiliar. transmitiría más significado y requeriría menos código.
haylem
@haylem: en un caso realmente simple, como este, estás agregando un método auxiliar, que alguien que lee el código tiene que ir a buscar. (Y alguien podría tener problemas con el método auxiliar y tener que descubrir todos los lugares desde los que se llama). Menos claridad, aproximadamente la misma cantidad de código. En un caso más complicado, iría con mi solución, luego con la de Tdammer . Respondí esta pregunta principalmente para señalar los problemas (ciertamente oscuros, pero interesantes) que las variables infrautilizadas plantean tanto a los humanos como a los optimizadores.
RalphChapin
@haylem: usted y tdammer dan la solución correcta. Solo creo que puede ser excesivo en algunos casos.
RalphChapin