Numeri magici, località e leggibilità

5

Ultimamente mi sono ritrovato a inserire numeri magici in codice per renderlo più leggibile. L'ho fatto in situazioni in cui il numero magico viene usato una sola volta e il suo scopo è ovvio dal contesto. Un esempio di un recente progetto:

/* Extract id from "/toClient/chat/id". */
String channelId = channelIdWithPath.split("/")[3];

Apparentemente, la "migliore pratica" è dichiarare il numero magico come una costante vicino all'inizio della classe, in questo modo:

private final int NUMBER_OF_DELIMITERS_BEFORE_ID_IN_CHAT_CHANNEL_PATH = 3;

... 100 lines of unrelated code ...

/* Extract id from "/toClient/chat/id". */
String channelId = channelIdWithPath.split("/")[NUMBER_OF_DELIMITERS_BEFORE_ID_IN_CHAT_CHANNEL_PATH];

Non vedo alcun vantaggio nel separare la dichiarazione della variabile dalla posizione in cui è stata utilizzata. Abbiamo praticamente raddoppiato il tempo necessario per comprendere questo codice e chiunque lo stia leggendo deve saltare oltre 100 righe per confermare che la variabile ha il valore corretto.

Potremmo dichiararlo come variabile locale appena prima che sia necessario, ma questo è ancora un "numero magico" almeno secondo CheckStyle. Questo è qualcosa che faccio spesso quando ritengo che il numero abbia bisogno di una descrizione (in questo caso una descrizione come "numero di delimitatori ..." è più difficile da capire rispetto al solo numero grezzo in uso).

Un'altra opzione è la logica di manipolazione separata in una funzione :

String channelId = getIdFrom(channelIdWithPath);

Questa astrazione nasconde i dettagli della manipolazione String e rimuove la necessità di un commento. Sfortunatamente dobbiamo ancora scrivere la funzione da qualche parte e lì vengono replicati i problemi originali (inclusa la necessità di documentare un percorso di esempio con un commento).

Se vogliamo dichiarare la variabile come costante E tenerla vicino alla funzione, dobbiamo creare un qualche tipo di classe StringManipulator per essa. Ora abbiamo aggiunto una classe per fare qualcosa che richiede 1 linea di codice. Mi sembra che questo tipo di approccio porti a programmi tentacolari dove

  • i singoli componenti di un programma diventano più facili da capire
  • la complessità strutturale e il flusso di esecuzione diventano più difficili da comprendere.

Ad esempio, se volessi leggere come viene recuperato channelId, dovrei prima passare a un altro metodo in un'altra classe, quindi dovrei saltare a dove è stata dichiarata la variabile. Tutto ciò potrebbe essere su un'unica riga di codice.

Modifica: questa risposta a una domanda simile si applica abbastanza bene per la mia particolare domanda.

    
posta Atte Juvonen 10.07.2016 - 21:04
fonte

4 risposte

12

In parole povere:

  • Se estrai il channelID in più punti, dovresti creare una funzione.
  • Tale funzione, essendo coesa, dovrebbe leggere dallo stato.
  • Parte di quello stato sarebbe la costante con un nome come DELIMITERS_BEFORE_ID .

Questo lo trovo:

String channelId = channelIDdWithPath.split("/")[DELIMS_BEFORE_ID];

... è più leggibile che questo

String channelId = channelIDWithPath.split("/")[3];

.. questo è ancora meglio:

String channelId = channelIDWithPath.split(PATH_DELIM)[DELIMS_BEFORE_ID];

.. ma è meglio:

String channelId = extractChannelID(channelIDWithPath);
  • Non è necessario verificare se il valore assegnato alle costanti o alle variabili di classe sono corretti ogni volta che si legge il codice. L'idea è che se le cose superano il test, puoi "astrarti" dal loro funzionamento interno. Il tuo cervello può gestire solo tanta complessità, quindi dovrai, col tempo, smettere di fare il tifo se il valore di DELIMS_BEFORE_ID è OK e iniziare a usare i pezzi lego senza fare attenzione se i blocchi lego contengono o meno la giusta quantità di acrilonitrile butadiene stirene.
risposta data 11.07.2016 - 14:23
fonte
9

Dovresti concentrarti su ciò che rende il tuo codice leggibile e lo spirito delle linee guida piuttosto che provare a dimostrare che puoi scrivere codice cattivo anche seguendo le linee guida.

Una costante denominata NUMBER_OF_DELIMITERS_BEFORE_ID_IN_CHAT_CHANNEL_PATH non è molto chiaro o comprensibile. Quindi sì, un numero magico non è probabilmente peggiore dell'uso di una costante così male nominata. Ma questo non è un argomento per usare numeri magici, questo è un argomento per usare nomi migliori per le tue costanti, o per riscrivere il codice per usare un approccio diverso.

Il tuo primo esempio di codice mostra un modo semplice per ottenere il terzo segmento. Ma presumibilmente hai bisogno anche degli altri segmenti di URL da qualche altra parte nell'app, quindi devi ripetere .split("/")[index] e il commento di accompagnamento ogni volta che ti serve un segmento. Questo interrompe il principio "non ripetere te stesso" e improvvisamente la logica di analisi degli URL è diffusa in tutta l'applicazione. Se lo schema dell'URL cambia (ad esempio, se viene introdotto un nuovo segmento tra il secondo e il terzo), devi aggiornare molti numeri e commenti, che sono soggetti a errori.

A proposito, non dovresti avere 100 righe di codice non correlato in un metodo o in una classe. Secondo il principio della responsabilità unica, tutto in una classe dovrebbe essere correlato, altrimenti dovrebbe essere suddiviso. Una variabile o una costante dovrebbe sempre essere dichiarata il più vicino possibile al suo utilizzo e nel minor spazio possibile.

Se hai la costante a livello di classe, ti suggeriamo di usarla in più punti per estrarre segmenti dal percorso. La soluzione qui è di incapsulare l'analisi dell'URL in un unico posto, in modo da utilizzare solo gli indici lì, e non esporli al programma in generale. Di nuovo il problema non è costanti contro letterali, ma incapsulamento.

    
risposta data 10.07.2016 - 21:13
fonte
1

Vedi il commento valido di Luc sulla tua domanda. Modelli di URL. Vorrei aggiungere ... ogni volta che vuoi un numero magico che non sia né 0 né 1 e non si riferisca nemmeno a una specifica funzione indipendente del dominio aziendale, cioè all'odore del codice.

Il tuo problema qui non sta nel fatto che stai codificando un numero magico tanto quanto stai codificando qualcosa che sta replicando informazioni che già esistono implicitamente. Se i tuoi modelli di URL cambiano, il tuo numero magico ora deve essere sincronizzato. Questo è un problema di manutenzione (minore), e questi si sommano e rendono più probabili gli errori.

    
risposta data 11.07.2016 - 15:25
fonte
-1

OK ci sono due domande in competizione qui. Uno è qual è il modo "migliore" "ufficiale" per programmare questo e Due è in realtà più leggibile

quindi ecco il mio modo "ufficiale" migliore

public class ChannelInfo
{
    public String ChannelId { get; set; }
}

public class PathExtractor : IPathExtractor
{
    private const int NUMBER_OF_DELIMITERS_BEFORE_ID_IN_CHAT_CHANNEL_PATH = 3;
    private const char SPLITCHAR = '/';
    public ChannelInfo GetChannelInfo(string path)
    {
        var info = new ChannelInfo();
        info.ChannelId = path.Split(new char[] { SPLITCHAR })[NUMBER_OF_DELIMITERS_BEFORE_ID_IN_CHAT_CHANNEL_PATH];
        return info;
    }
}

Ora, è "migliore" e "più leggibile (tm)" di

/* Extract id from "/toClient/chat/id". */
String channelId = channelIdWithPath.split("/")[3];

Dico NO !! per favore! controlla il tuo shock e la rabbia caro lettore, mentre aggiungo alcune condizioni ....

  • Se utilizzi il codice in molti punti, il primo metodo potrebbe salvarti alcuni caratteri

  • Se hai più implementazioni di IPathExtractor il primo metodo è più flessibile

  • Se disponi di più proprietà che puoi aggiungere a ChannelInfo, il primo metodo diventa più facile da usare

Quindi, in generale, sì è probabilmente una buona idea dividere la logica in classi ecc.

MA

  • Se hai un solo punto in cui estrai un singolo valore come parte di una funzione più grande e viene commentato

Quindi no, non è particolarmente più leggibile o più facile se lo dividi in un'interfaccia più un'implementazione più un oggetto di ritorno, oltre a tutte le proprietà e i metodi ecc.

Avrai aggiunto una tonnellata di codice in più solo per poter dire che stai selezionando "clean coding" e spuntando le caselle di controllo dello stile

    
risposta data 11.07.2016 - 15:02
fonte

Leggi altre domande sui tag