Metti la logica condizionale nel metodo per DRY o tienilo all'esterno per la leggibilità?

1

Prendi il seguente esempio che carica un annuncio interstitial ogni 10 volte che l'utente fa XYZ nell'app, a determinate condizioni. Viene chiamato in più punti nella base di codice:

public class AdHandler {

    public void showInterstitialAd() {

        if (Subscription.isSubscriptionActive()) {
            Log.i(TAG, "Subscription active, not loading interstitial ad");
            return;
        }

        if (!EventTracker.isEventThresholdMet()) {
            Log.i(TAG, "Threshold not yet met, not loading interstitial ad");
            return;
        }

        ... potentially more conditionals that return early if not met

        // Finally create and show the interstitial ad because the conditional checks have passed
    }
}

In questa versione, tutta la logica di caricare o meno l'annuncio è dentro il metodo che carica l'annuncio, il che sembra logico. E supponendo che chiamo showInterstitialAd() in più punti nella base di codice, è ASCIUTTO perché non sto controllando isSubscriptionActive() e isEventThresholdMet() in più punti (e potrebbero esserci ottime condizioni più / logica da valutare).

La mia unica preoccupazione è che dal punto di vista esterno, senza guardare effettivamente la dichiarazione di showInterstitialAd() , qualsiasi chiamata a showInterstitialAd() sembra che l'annuncio sia stato caricato ogni volta , senza condizioni intorno al carico. Anche se questa non è la cosa peggiore, si può immaginare che avere più di 50 metodi che seguono questo paradigma declare-condizionale-logico all'interno può rendere il codice base un po ' convoluti a prima vista e difficili da seguire senza che le definizioni dei metodi siano sempre disponibili come riferimento.

Se guardiamo l'altra versione, sarebbe qualcosa del genere:

public void firstMethod() {

    ... more code above

    if (!isSubscriptionActive() && isEventThresholdMet()) {
        showInterstitialAd();
    }
}

public void secondMethod() {

    if (!isSubscriptionActive() && isEventThresholdMet()) {
        showInterstitialAd();
    }

    ... more code below
}

Questa versione rende ovvio quali condizioni causano il codice , ma è anche una delle principali cause di duplicazione della logica condizionale. Il mio istinto dice che la versione 1 è più manutenibile a lungo termine, specialmente se showInterstitialAd() è chiamato in molti posti, anche se la versione 2 sembra più leggibile a prima vista.

C'è qualche consiglio / ragionamento da parte di persone più anziane, o libri come Clean Code che possono dare alcune ragioni per cui un approccio è migliore dell'altro? Sto specificatamente cercando un ragionamento in termini di leggibilità e manutenibilità.

    
posta Chris Cirefice 19.03.2018 - 21:27
fonte

4 risposte

7

Penso che la logica nel codice esistente sia valida. Penso che il nome del metodo sia il problema.

Invece di nominarlo ShowInterstatialAd, perché non nominarlo qualcosa che dice cosa fa il metodo, come tryToShowInterstatialAd (o qualcosa di meno formulato in modo imbarazzante con la stessa idea)?

    
risposta data 19.03.2018 - 21:31
fonte
0

Sono d'accordo che l'opzione # 2 sarebbe una violazione del principio DRY (specialmente se disseminata su centinaia di classi), ma nell'opzione # 1 la funzione sta - o almeno fibs - su ciò che fa: in realtà non lo fa mostra sempre un annuncio interstitial.

Mentre sono d'accordo con CandiedOrange che nomi come showInterstitialAdMaybe() sono noise senza senso (cosa significa forse? quando le stelle hanno ragione?) non significa che dovresti omettere il fatto piuttosto importante che non sempre mostra il anno Domini. Hai bisogno di un nome di funzione migliore.

Forse dividi showInterstitialAd() in due funzioni: una che mostra la pubblicità (sempre!) chiamata ... showInterstitialAd() e una che determina se gli annunci devono essere mostrati prima di chiamare showInterstitialAd() . Forse qualcosa sulla falsariga di showInterstitialAdIfNecessary() con un commento dettagliato quali sono i mezzi necessari nella descrizione della funzione.

    
risposta data 20.03.2018 - 13:38
fonte
0

My one concern is that from the outside perspective, without actually looking at showInterstitialAd()'s declaration, any call to showInterstitialAd() looks like the ad is being loaded every time, with no conditions around the loading.

No, non lo è.

Non posso davvero sottolineare abbastanza. Non hai il diritto di presumere che qualsiasi metodo chiamato showInterstitialAd() faccia effettivamente quello che dice. Perché? Astrazione. Qualunque implementazione tu stia parlando ha il diritto di decidere come rispondere.

Se non fare nulla non è un fallimento, non ha alcun obbligo di fare qualcosa. Puoi vedere questa idea nel Pattern degli oggetti nulli .

Quando chiami showInterstitialAd() sei inviando un comando che non annuncia un evento .

Se il nome era stato interstitialAdShown() Quindi, dannazione, è meglio che sia stato mostrato con successo. Ma showInterstitialAd() è un comando. I comandi possono essere rifiutati. Possono essere rifiutati con eccezioni, messaggi di errore o semplicemente non facendo nulla.

Nel mondo degli oggetti showInterstitialAdd() è un messaggio. Esattamente ciò che la risposta a quel messaggio non è qualcosa che si arriva a sapere dal nome. Se lo hai fatto, i dettagli di implementazione stanno uscendo da questa astrazione.

Quindi nomi come showInterstitialAddMaybe() stanno aggiungendo rumore senza senso. Dovresti già sapere che non ti è stato garantito che qualcosa accada. Solo quale messaggio verrà inviato.

Immagina di dirti di saltare e tu mi ignori. Significa che ho sbagliato il nome del comando o vuol dire che non sono il tuo sergente istruttore? Supponiamo che per progetto si salti solo quando viene detto il sabato. Sei in debito con qualcuno per spiegarti perché non stai saltando martedì? Dovrei capire la regola del sabato per usarti senza farmi delle eccezioni? L'astrazione e il polimorfismo funzionano solo quando non si richiede ai clienti di comprendere i dettagli dell'implementazione. Quindi smetti di pensare che il nome narra l'intera storia.

Gli esempi includono nomi come open() non openIfFileExistsOtherwiseThrow() e System.out.println() non System.out.printlnButOnlyIfOutIsNotDisablingPrinting()

Smetti di far trapelare i dettagli di implementazione nei tuoi nomi. I nomi dovrebbero trasmettere intenti, non risultati.

Se hai bisogno di mostrare la natura condizionale al livello attuale di astrazione, l'unica scusa è se vuoi mostrare QUALI condizionali sono rilevanti.

if ( isEventThresholdMet() ) { 
    showInterstitialAdToGuests()
}

Questo mi dice molto più di verruche insignificanti come "forse" che mi ricordano solo che potrebbe non fare nulla. Ogni comando potrebbe non fare nulla.

    
risposta data 20.03.2018 - 06:41
fonte
-3

!isEventThresholdMet() è sbagliato.

Se qualcosa è un evento, allora in nessun punto del codice dovrebbe controllare se l'evento dovrebbe già essere stato attivato.

Dovresti attivare un evento quando viene raggiunta la soglia e chiamare quindi i gestori per quell'evento.

Questo è il modo di gestire gli eventi, facendo qualcos'altro porta a strani codici in cui si duplica la logica ovunque.

    
risposta data 19.03.2018 - 21:35
fonte

Leggi altre domande sui tag