Come denominare un metodo che può o meno eseguire un'azione a seconda di una condizione?

7

Mi imbatto in questo caso un po 'spesso, e sono sorpreso di trovare così poche discussioni simili sul web. Questa domanda è molto correlata, ma il mio problema è che voglio un metodo che faccia il più generale "fai X se Y" piuttosto che "fai X se necessario" . La risposta in questo link è usare il prefisso Ensure , ma quella parola non si adatta se assicurare X non è l'intenzione del metodo.

Lo scenario che ho in mente è questo:

void mayPerformAction() {
    // Do some preparatory calculations
    // ...
    if (shouldPerform) {
        // Perform action
        // ...
    }
}

Il motivo per cui non sto usando due metodi separati ( shouldPerformAction() e performAction() ) è perché sia la condizione che l'azione dipendono da alcuni calcoli preparatori, che dovrebbero essere ripetuti altrimenti. Ora, la mia domanda è: qual è il nome più logico e leggibile per il metodo mayPerformAction() ?

Per chiarire, è importante per il chiamante che l'azione possa talvolta non essere eseguita, altrimenti mi sembra logico usare performAction() .

Ammetto che questa è una specie di problema XY, e ci sono più soluzioni pubblicate, ognuna delle quali ha buoni argomenti a favore e contro di loro. Per riassumere:

  • Estrarre il dubbio e assegnargli un nome meno dettagliato, ad es. solo performAction() .
  • Preferisci chiarezza e fai i calcoli due volte; la differenza di prestazioni sarà comunque trascurabile in molti casi: if (shouldPerform()) performAction() .
  • Come sopra, ma memorizza il risultato condiviso dei calcoli in una variabile globale o lo restituisce (preferisco quest'ultimo) quindi nessuna risorsa viene sprecata.

Ritengo che l'approccio migliore dipenda da quanto "grave" sia la condizione e da quanto costosi siano i calcoli preparatori; come tale sto lasciando la domanda senza risposta per ora.

    
posta aviator 04.02.2018 - 22:56
fonte

6 risposte

12

Sei intrappolato in un modo di pensare strutturale. Un nome dovrebbe astrarre i dettagli di implementazione. Non dovrebbe diventare una mano breve per loro.

 IfBirthdayBuyCake();

è un nome terribile. Espone i dettagli dell'implementazione come se li avessi scritti qui nudi.

Raggiungerei un nome migliore e una migliore astrazione.

celebrateHoliday();

Da qualche parte in esso probabilmente troverai qualcosa di simile

if ( birthday() ) {
    buyCake();
}

e forse qualche altra vacanza. Quando lo chiami, potrebbe non fare nulla. Il che significa che quando lo chiami non devi sapere se oggi è una vacanza. Quel non sapere ti libera da quel dettaglio. Questo è quello che dovrebbe fare un buon nome.

    
risposta data 05.02.2018 - 00:19
fonte
2

Consiglierei di non mettere condizionali nei nomi dei metodi. Se vuoi che il codice chiamante venga letto come un condizionale, usa un condizionale:

if (cakeIsNeeded) buyCake();

Oppure, se gli operatori ternari o i cortocircuiti sono la tua passione:

cake = cake == null ? buyCake() : cake;
cake = cake || buyCake();

Altrimenti, puoi ignorare silenziosamente le chiamate ripetute, utilizzare memoization o generare eccezioni nel metodo per gestire la ripetizione chiama - qualsiasi cosa ritenga più appropriata e meno sorprendente per il particolare metodo.

Se hai un nome di metodo che sembra ripetere le chiamate vorrebbe ripetere le loro azioni, ma non , un modo per battere la "sorpresa" è aggiungere un force facoltativo, un skipCache o un parametro booleano simile. (Il nome del flag deve essere pertinente al nome del metodo e / o alla skip-logic.)

Tutto ciò che ho detto, tenderei a cercare un verbo che implichi ciò che il chiamante ha bisogno di , piuttosto che il metodo . Nell'esempio della torta, il chiamante vuole cake e non gli importa molto da dove viene. Mi sembra che tu voglia solo getCake() o findCake() .

Entrambi i nomi comunicano che verrà restituito Cake . Non rivelano al chiamante come verrà localizzato quel Cake . Potrebbe essere acquistato, preso dal banco o prodotto da elfi magici. Questi sono dettagli di implementazione.

Un avvertimento importante a tutto questo: questi schemi di denominazione tendono ad essere altamente idiomizzati. Fai riferimento alle librerie interne della tua lingua per esempi di come loro gestiscono questo. E parla con il tuo team per decidere i tuoi idiomi interni .

    
risposta data 05.02.2018 - 17:57
fonte
1

If there is a better alternative that still prevents double code execution, feel free to let me know :)

Se preparatory calculations è molto costoso e calcolarlo due volte farebbe male alla performance (quindi non è un caso di prematuro ottimizzazione ) Vorrei ancora separare le condizioni e l'azione in due metodi perché legge

if (shouldPerform()) doPerformAction()

è molto più intuitivo. Per far fronte al calcolo dello stato puoi racchiudere il preparatory calculations nello stato della classe in questo modo:

public class MyClass {
    private CalculationResult preparatoryCalculationResult = null;

    public boolean shouldPerform() {
        initIfNeccessary();
        return preparatoryCalculationResult.shouldPerform();
    }

    public void doPerformAction() {
        initIfNeccessary();
        preparatoryCalculationResult.doPerformAction();
    }

    private void initIfNeccessary() {
        if (preparatoryCalculationResult == null) {
            preparatoryCalculationResult = 42; // very expensive calculation ;-)
        }
    }

}
    
risposta data 05.02.2018 - 16:35
fonte
0

Ragionevolmente comune è "performActionIfNeeded" (un metodo che capirà abilmente se un'azione è necessaria e lo esegue solo in quel caso) rispetto a "performActionIfWanted" (un metodo che capirà in modo intelligente che si vuole eseguire un'azione e solo eseguirlo in quel caso).

    
risposta data 05.02.2018 - 01:54
fonte
0

In corso:

To clarify, it is important to the caller that the action may sometimes not be executed, otherwise it seems logical to me to use performAction().

Sembra che chi chiama ha una certa conoscenza di ciò che accadrà. Penso che sia quindi giustificato che il chiamante possa decidere se l'azione debba essere eseguita o meno.

Un esempio:

Una funzione dovrebbe cancellare tutti i file che corrispondono a un modello, ma solo se la loro dimensione totale supera un certo valore. La scansione è costosa, ma deve essere eseguita per verificare le condizioni. L'azione di cancellazione richiede la conoscenza della scansione per evitare la scansione due volte.

Vorrei semplicemente dividerlo in due modi:

// Returns a ScanResult containing files matching a pattern
ScanResult performScan(Pattern pat);  

// Deletes a list of files
void delete(List<File> files);

La condizione può ora essere facilmente esternalizzata:

ScanResult result = obj.performScan(...)

if(result.totalSize() > 100 * 1024) {
    obj.delete(result.getFiles());
}
    
risposta data 06.02.2018 - 14:36
fonte
0

Una parte del problema è il tipo di ritorno void del metodo originale. Sarebbe meglio restituire una sorta di oggetto struct del risultato al chiamante per indicare diversi gradi di successo. Ciò fornisce al chiamante le informazioni su cosa è successo o meno, nel caso in cui abbia bisogno di sapere di restituire il messaggio all'interfaccia utente o qualcosa di simile. Inoltre, segnala rapidamente / chiaramente al coder successivo di toccare il codice che il metodo chiamato potrebbe non eseguire operazioni pesanti con ogni chiamata.

public class OperationResult
{
    public OperationState State { get; set; }
    public string OperationMessage { get; set; } // result codes, etc
    public enum OperationState { NoOperationPeformed, OperationSuccessful } // can have more if needed
}

Potresti restituire l'intera classe sopra, se hai bisogno del OperationMessage (o di altri dati sulla chiamata), o solo del enum OperationState se vuoi solo segnalare che il lavoro 'reale' della funzione è stato saltato .

È possibile che un altro coder possa assumere da quel void return che il metodo fa sempre la stessa cosa. Restituire quanto sopra potrebbe aiutare a capire tramite IDE intellisense che ci sono altre possibilità.

    
risposta data 06.02.2018 - 19:13
fonte

Leggi altre domande sui tag