Meglio avere 2 metodi con un significato chiaro, o solo 1 metodo a duplice uso?

29

Per semplificare l'interfaccia, è meglio non avere il metodo getBalance() ? Il passaggio di 0 a charge(float c); darà lo stesso risultato:

public class Client {
    private float bal;
    float getBalance() { return bal; }
    float charge(float c) {
        bal -= c;
        return bal;
    }
}

Forse prendi nota in javadoc ? Oppure, lascia che sia l'utente della classe a capire come ottenere il saldo?

    
posta david 05.02.2016 - 20:30
fonte

6 risposte

89

Sembra che tu suggerisca che la complessità di un'interfaccia è misurata dal numero di elementi che ha (metodi, in questo caso). Molti sostengono che il fatto di dover ricordare che il metodo charge può essere utilizzato per restituire il saldo di un Client aggiunge molta più complessità rispetto all'elemento extra del metodo getBalance . Rendere le cose più esplicite è molto più semplice, specialmente al punto in cui non lascia ambiguità, indipendentemente dal numero più alto di elementi nell'interfaccia.

Inoltre, la chiamata di charge(0) viola il principio di minimo stupore , noto anche come metrica WTFs al minuto (da Codice pulito, immagine sotto), che rende difficile per i nuovi membri del team (o quelli attuali, dopo un po 'di distanza dal codice) fino a quando non capiscono che la chiamata è effettivamente utilizzata per ottenere l'equilibrio. Pensa a come reagirebbero gli altri lettori:

Inoltre,lafirmadelmetodochargevacontrolelineeguidadi facendo una e una sola cosa e separazione comando-query , perché fa sì che l'oggetto cambi il suo stato mentre restituisce anche un nuovo valore .

Tutto sommato, credo che l'interfaccia più semplice in questo caso sarebbe:

public class Client {
  private float bal;
  float getBalance() { return bal; }
  void charge(float c) { bal -= c; }
}
    
risposta data 05.02.2016 - 20:41
fonte
28

IMO, sostituire getBalance() con charge(0) nell'intera applicazione non è una semplificazione. Sì, sono meno righe, ma offusca il significato del metodo charge() , che potrebbe potenzialmente causare mal di testa lungo la linea quando tu o qualcun altro hai bisogno di rivisitare questo codice.

Sebbene possano dare lo stesso risultato, ottenere il saldo di un account non equivale a un addebito pari a zero, quindi sarebbe probabilmente meglio separare le tue preoccupazioni. Ad esempio, se hai mai avuto bisogno di modificare charge() per accedere ogni volta che c'è una transazione dell'account, ora hai un problema e dovresti comunque separare la funzionalità.

    
risposta data 05.02.2016 - 20:43
fonte
13

È importante ricordare che il tuo codice dovrebbe essere auto-documentante. Quando chiamo charge(x) , mi aspetto che venga addebitato x . Le informazioni sull'equilibrio sono secondarie. Inoltre, potrei non sapere come viene implementato charge() quando lo chiamo e sicuramente non saprò come verrà implementato domani. Ad esempio, considera questo potenziale aggiornamento futuro a charge() :

float charge(float c) {
    lockDownUserAccountUntilChargeClears();
    bal -= c;
    Unlock();
    return bal;
}

Improvvisamente usando charge() per ottenere che il saldo non appaia così buono.

    
risposta data 06.02.2016 - 04:10
fonte
2

Usare charge(0); per ottenere l'equilibrio è una cattiva idea: un giorno qualcuno potrebbe aggiungere del codice lì per registrare gli addebiti effettuati senza rendersi conto dell'altro uso della funzione, e quindi ogni volta che qualcuno ottiene l'equilibrio essere registrato come una tassa. (Ci sono modi per aggirare questo come un'istruzione condizionale che dice qualcosa del tipo:

if (c > 0) {
    // make and log charge
}
return bal;

ma questi si basano sul programmatore che sa di implementarli, cosa che non farà se non è immediatamente evidente che sono necessari.

In breve: non fare affidamento sui tuoi utenti o sul tuo programmatore che realizzano che charge(0); è il modo corretto per ottenere il saldo, perché a meno che non ci sia la documentazione che è sicuro non perdere, francamente sembra il più modo spaventoso di ottenere l'equilibrio possibile.

    
risposta data 06.02.2016 - 12:07
fonte
0

So che ci sono molte risposte, ma un'altra ragione contro charge(0) è che un semplice errore di charge(9) farà sì che il saldo del tuo cliente diminuisca ogni volta che vuoi ottenere il saldo. Se hai un buon test unitario potresti essere in grado di mitigare quel rischio, ma se non riesci a essere diligente su ogni chiamata a charge potresti avere questo contrattempo.

    
risposta data 06.02.2016 - 20:02
fonte
-2

Vorrei menzionare un caso particolare in cui avrebbe senso avere meno metodi, più multiuso: se c'è un sacco di polimorfismo, cioè molte implementazioni di questa interfaccia ; specialmente se tali implementazioni sono in codice sviluppato separatamente che non può essere aggiornato in sync (l'interfaccia è definita da una libreria).

In tal caso, semplificare il lavoro di scrittura di ogni implementazione è molto più prezioso della chiarezza di utilizzo di esso, perché il primo evita i bug di violazione del contratto (i due metodi sono incoerenti tra loro), mentre il secondo solo la leggibilità , che può essere recuperato da una funzione helper o da un superclasse che definisce getBalance in termini di charge .

(Questo è uno schema di progettazione, che non richiama un nome specifico per: definire un'interfaccia complessa e amichevole per il chiamante in termini di uno strumento minimale per l'implementazione.) In Classic Mac OS l'interfaccia minima per le operazioni di disegno è stata chiamata "bottleneck" ma questo non sembra essere un termine popolare.)

Se questo non è il caso (ci sono poche implementazioni o esattamente una), separare i metodi per chiarezza e consentire la semplice aggiunta di comportamenti rilevanti per gli addebiti diversi da zero a charge() , ha senso.

    
risposta data 06.02.2016 - 06:00
fonte

Leggi altre domande sui tag