Generalizza l'utilizzo variabile all'interno del codice


11

Vorrei sapere se è una buona pratica generalizzare le variabili (usare una singola variabile per memorizzare tutti i valori).
Prendi in considerazione un semplice esempio

 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 ;

e

 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 ;

Nel primo caso uso 4 stringhe ognuna delle quali memorizza dati per eseguire le azioni menzionate nei loro suffissi.
Nel secondo caso solo 1 variabile per memorizzare tutti i tipi di dati.
Avere variabili diverse semplifica la lettura e la comprensione da parte di qualcun altro. Ma averne troppi ne rende difficile la gestione.

Inoltre, avere troppe variabili ostacola le mie prestazioni?

PS: per favore non rispondere al codice, ad esempio era solo per comunicare ciò che intendo davvero.


Ovviamente riutilizzi la stessa variabile ... perché l'hai definita in una funzione. Ecco a cosa servono le funzioni.
zzzzBov,

Risposte:


26

Dover farti questa domanda è un odore piuttosto forte che non stai seguendo DRY (non ripetere te stesso). Supponiamo di avere questo, in un ipotetico linguaggio a parentesi graffa:

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);
}

Rifattorizza questo in:

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");
}

Si noti come scompare la necessità di decidere se utilizzare o meno variabili diverse e come ora è possibile modificare la logica per l'esecuzione di una query e la stampa del risultato in un'unica posizione, anziché dover applicare la stessa modifica tre volte. (Ad esempio, potresti decidere di voler pompare il risultato della query attraverso un sistema modello invece di stamparlo immediatamente).


2
Adoro il principio DRY :)
artjom,

1
@tdammers è bello avere solo 2 righe di codice all'interno di una funzione? considera se ho questa funzione doFoo () {print (runQuery ("Selct a, b, c from XYZ"));}
Shirish11

1
No, lo stack di chiamate non aumenta: ogni chiamata runAndPrintspinge un frame dello stack quando lo chiami, quindi lo riavvia quando esce la funzione. Se lo chiami tre volte, farà tre coppie push / pop, ma lo stack non cresce mai di più di un fotogramma alla volta. Dovresti davvero preoccuparti solo della profondità dello stack di chiamate con funzioni ricorsive.
tdammers,

3
E le funzioni con solo due righe di codice vanno benissimo: se due righe formano un'unità logica, allora lo sono due righe. Ho scritto molte funzioni one-liner, solo per mantenere un po 'di informazioni isolate e in un unico posto.
tdammers,

1
@JamesAnderson: è un esempio un po 'inventato, ma serve a illustrare un punto. Non si tratta di quante righe di codice hai. È quante volte affermi lo stesso fatto. Ecco di cosa si tratta DRY, così come il principio Single Source Of Truth, la regola Non copiare e incollare, ecc.
tdammers,

14

Normalmente, questa è una cattiva pratica.

Riutilizzare una variabile in questo modo può rendere il codice che confonde leggere per capire.

Coloro che leggono il codice non si aspettano che una variabile venga riutilizzata in questo modo e non sanno perché un valore impostato all'inizio ha un valore diverso alla fine della funzione.

Gli esempi che hai pubblicato sono molto semplici e non soffrono davvero di questo problema, ma non sono rappresentativi di un codice che riutilizza le variabili (dove è impostato all'inizio, viene riutilizzato da qualche parte nel mezzo - fuori dalla vista).

Gli esempi che hai fornito si prestano all'incapsulamento in funzioni, in cui dovrai passare la query ed eseguirla.


che ne è delle prestazioni del sistema?
Shirish11

@ Shirish11 - Potrebbe. Dipende dal compilatore, dalla lingua, dall'ambiente e da altre variabili.
Oded,

Di solito, il compilatore è bravo a ottimizzarlo. Tuttavia, dipende sempre dal compilatore / plateform / caso specifico / configurazione.
deadalnix,

7

Il codice auto-documentato è più facile da leggere e mantenere

Segui il principio del minimo stupore e del precetto del codice come documentazione : usa una variabile per un obiettivo, sia per renderlo facile da capire sia per il codice facile da leggere senza spiegazioni.

Il codice correttamente strutturato è più facile (quindi più economico) da (ri) utilizzare

Inoltre, qui sembrerebbe che querysia sempre usato per preparare un'istruzione prima di eseguirla. Questo è probabilmente un segno che si vuole refactoring parte di questo codice in uno (o più) metodi di supporto per preparare ed eseguire la query (per conformarsi al principio DRY ).

In questo modo, efficacemente:

  • usa solo una variabile nel tuo metodo di supporto per identificare la query del contesto corrente,
  • è necessario digitare meno codice ogni volta che si desidera rieseguire una query,
  • rendere il tuo codice più leggibile per gli altri.

Esempi:

Considera questo, preso dal tuo esempio, dove la versione refactored è ovviamente migliore. Ovviamente il tuo frammento era solo un esempio ai fini di questa domanda, ma il concetto è ancora valido e in scala.

Il tuo esempio 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 ;

Il tuo esempio 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 ;

Esempio 3 (pseudo-codice refactored):

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 ...');

Il vantaggio si manifesta con un riutilizzo regolare.

Aneddoto personale

Inizialmente ho iniziato come programmatore in C lavorando con proprietà dello schermo limitate, quindi riutilizzare le variabili aveva senso sia per il codice compilato (allora) che per consentire la lettura di più codici contemporaneamente.

Tuttavia, dopo essere passato a linguaggi di livello superiore e approfondito la programmazione funzionale, ho preso l'abitudine di utilizzare variabili immutabili e riferimenti immutabili, ove possibile, per limitare gli effetti collaterali.

Che vantaggio ne ricavo?

Se prendi l'abitudine di rendere immutabili tutti gli input della tua funzione e di restituire un nuovo risultato (come farebbe una vera funzione matematica), prendi l'abitudine di non duplicare i negozi.

Per estensione, questo porta a:

  • stai scrivendo brevi funzioni,
  • con obiettivi ben definiti,
  • che sono più facili da capire,
  • riutilizzare,
  • estendere (sia per eredità OO che per concatenamento funzionale),
  • e documentare (come già auto-documentante).

Non sto dicendo che non ci sia alcun vantaggio nello stato mutevole qui, sto solo sottolineando come l'abitudine potrebbe crescere su di te e come influisce sulla leggibilità del codice.


2

In termini di progettazione del codice

In generale, va bene riutilizzare le variabili per memorizzare valori diversi - dopotutto, è per questo che vengono chiamate variabili, perché il valore in esse memorizzato varia - purché il valore non sia solo dello stesso tipo ma significhi anche la stessa cosa . Ad esempio ovviamente va bene riutilizzare la currentQueryvariabile qui:

for currentQuery in queries:
    execute query;

Naturalmente c'è un ciclo in modo da avere di riutilizzare una variabile, ma anche se non ci fosse un anello sarebbe stato a posto. Se il valore non significa la stessa cosa, utilizzare una variabile separata.

In particolare, tuttavia, il codice che stai descrivendo non ha un bell'aspetto, ma si ripete . È molto meglio usare un metodo loop o helper (o entrambi). Personalmente ho visto molto raramente un codice di produzione che assomiglia alla tua prima o seconda versione, ma nei casi che ho, penso che la seconda versione (riutilizzo variabile) fosse più comune.

In termini di prestazioni

Dipende dalla lingua, dal compilatore (i) e dai sistemi di runtime utilizzati, ma in generale non dovrebbe esserci alcuna differenza - in particolare i compilatori per macchine di registro basate su stack (come il popolare x86 / x86-64) saranno comunque solo usa qualsiasi memoria dello stack o registro libera possibile come destinazione del compito, ignorando completamente se desideri la stessa variabile o meno.

Ad esempio, gcc -O2genera lo stesso binario esatto e l'unica differenza di prestazioni che conosco è la dimensione della tabella dei simboli durante la compilazione - completamente trascurabile a meno che non si torni indietro negli anni '60.

Un compilatore Java genererà bytecode che necessita di più spazio di archiviazione per la prima versione, ma il jitter della JVM lo rimuoverà comunque, quindi sospetto che non ci sarebbe praticamente alcun impatto sulle prestazioni evidente anche se è necessario un codice altamente ottimizzato.


0

Penso che riutilizzare la variabile vada bene per la maggior parte del tempo.

Per quanto mi riguarda, utilizzo semplicemente la variabile di query per la maggior parte del tempo. Quasi sempre eseguo la query subito dopo. Quando non eseguo subito la query, di solito utilizzo un nome di variabile diverso.


-1

Può aumentare l'utilizzo dello stack se il compilatore è particolarmente stupido. Personalmente non penso che avere una variabile separata per ogni query si aggiunga a qualsiasi leggibilità, devi comunque guardare la stringa della query per vedere cosa fa.


Ho appena fornito un semplice esempio in modo che sia più facile per i lettori capire cosa cerco. Il mio codice è molto più complesso di così.
Shirish11

-2

Nell'esempio, vorrei andare con il secondo esempio. È abbastanza chiaro sia per un lettore che per gli ottimizzatori cosa stai facendo. Il primo esempio è un po 'più appropriato, e con un codice un po' più complicato lo userei, ma lo farei così:

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

(A questo punto, potrei considerare la soluzione di tdammers .)

Il problema con il primo esempio è che querycrerientra nell'ambito dell'intero blocco, che potrebbe essere esteso. Questo può confondere qualcuno che legge il codice. Può anche confondere gli ottimizzatori, che potrebbero lasciare in una scrittura di memoria non necessaria, quindi querycreè disponibile in seguito, se necessario (che non lo è). Con tutte le parentesi graffe, queryviene memorizzato solo in un registro, se quello.

Con frasi come "Crea tabella" ed "Esegui" non mi sembra che qui si noterà una scrittura in più di memoria, quindi mi limiterei a criticare il codice per confondere il lettore. Ma è utile essere consapevoli di questo codice se si sta scrivendo in cui la velocità non importa.


Non sono d'accordo. Se si dovesse preferire il secondo esempio per chiarezza, dovrebbe essere sottoposto a refactoring alle chiamate successive a un metodo helper. trasmetterebbe più significato e richiederà meno codice.
Hayylem,

@haylem: In un caso davvero semplice, come questo, stai aggiungendo un metodo helper, che qualcuno deve leggere e trovare. (E qualcuno potrebbe avere problemi con il metodo helper e capire tutti i luoghi da cui viene chiamato.) Meno chiarezza, circa la stessa quantità di codice. In un caso più complicato, andrei con la mia soluzione, poi con quella di Tdammer . Ho risposto a questa domanda principalmente per sottolineare i problemi (dichiaratamente oscuri, ma interessanti) che le variabili sottoutilizzate pongono sia agli umani che agli ottimizzatori.
RalphChapin,

@haylem: tu e tdammer avete entrambi la soluzione corretta. Penso solo che possa essere eccessivo in alcuni casi.
RalphChapin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.