Progettazione di metodi correlati al database, che è meglio restituire: true / false o row affected?

10

Ho alcuni metodi che eseguono alcuni cambiamenti di dati in un database (inserire, aggiornare ed eliminare). ORM Sto utilizzando i valori int interessati dalla riga di restituzione per quel tipo di metodo. Cosa devo restituire per "il mio metodo", al fine di indicare lo stato di successo / fallimento dell'operazione?

Considera il codice che restituisce un int :

A.1

public int myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows;
}

Quindi uso:

A.2

public void myOtherMethod() {
    ...
    int affectedRows = myLowerLevelMethod(id)

    if(affectedRows > 0) {
        // Success
    } else {
        // Fail
    }
}

Confronta con booleano:

B.1

public boolean myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows > 0;
}

Quindi uso:

B.2

public void myOtherMethod() {
    ...
    boolean isSuccess = myLowerLevelMethod(id)

    if(isSuccess) {
        // Success
    } else {
        // Fail
    }
}

Quale (A o B) è migliore? O pro / contro di ciascuno?

    
posta Hoang Tran 09.07.2014 - 09:30
fonte

6 risposte

18

Un'altra opzione è restituire un oggetto risultato invece dei tipi base. Ad esempio:

OperationResult deleteResult = myOrm.deleteById(id);

if (deleteResult.isSuccess()) {
    // ....
}

Con questo, se per qualche motivo hai bisogno di restituire il numero di righe interessate, puoi semplicemente aggiungere un metodo in OperationResult:

if (deleteResult.isSuccess()) {
    System.out.println("rows deleted: " + deleteResult.rowsAffected() );
}

Questo design consente al tuo sistema di crescere e includere nuove funzionalità (conoscendo le righe interessate) senza modificare il codice esistente.

    
risposta data 09.07.2014 - 12:32
fonte
12

Restituire il numero di righe interessate è migliore perché fornisce ulteriori informazioni su come l'operazione è proseguita.

Nessun programmatore ti biasimerà perché dovrà scriverlo per verificare se ha ottenuto alcune modifiche durante l'operazione:

if(affectedRows > 0) {
    // success
} else {
    // fail
}

ma ti biasimeranno quando dovranno conoscere il numero di righe interessate e si renderanno conto che non esiste un metodo per ottenere quel numero.

BTW: se per "fallimento" si intende un errore di query sintattica (nel qual caso il numero di righe interessate è ovviamente pari a 0), allora il lancio dell'eccezione sarebbe più appropriato.

    
risposta data 09.07.2014 - 10:38
fonte
10

Non consiglierei nessuno di loro. Invece, non restituire nulla (void) in caso di successo e generare un'eccezione in caso di errore.

Questo è esattamente lo stesso motivo per cui scelgo di dichiarare alcuni membri della classe privata. Rende anche la funzione più facile da usare. Più contesto operativo non sempre significa migliore, ma certamente significa più complesso. Meno prometti, più ti allontani, più è facile per il cliente capire e più libertà hai nel scegliere come implementarlo.

La domanda è come indicare il successo / errore. In questo caso è sufficiente segnalare il fallimento lanciando un'eccezione e non restituire nulla in caso di successo. Perché devo fornire più delle esigenze dell'utente?

Possono verificarsi guasti / situazioni eccezionali e quindi dovrai affrontarli. Se si utilizza try / catch per farlo o esaminare i codici di ritorno, è una questione di stile / preferenza personale. Le idee alla base di try / catch sono: separate il flusso normale da un flusso eccezionale e lasciano che le eccezioni raggiungano il livello in cui possono essere gestite più appropriatamente. Quindi, come molti hanno già sottolineato, dipende dal fatto che il fallimento sia davvero eccezionale o no.

    
risposta data 09.07.2014 - 11:58
fonte
2

"È meglio di così?" non è una domanda utile quando le due alternative non fanno la stessa cosa.

Se hai bisogno di conoscere il conteggio delle righe interessate, allora devi usare la versione A. Se non devi, puoi usare la versione B - ma qualsiasi vantaggio tu possa guadagnare in termini di meno sforzo di scrittura del codice è già andato da quando ti sei preso la briga di postare entrambe le versioni in un forum online!

Il mio punto è: quale soluzione è meglio dipende interamente da quali siano le vostre esigenze specifiche per questa applicazione, e voi conoscete queste circostanze molto meglio di noi. Non esiste una scelta migliore per l'intero settore, sicura per l'uso, best-practice, non-get-fir-over-it che è in generale ; devi pensarci da solo E per una decisione facilmente rivisitata come questa, non è necessario passare troppo tempo a pensarci.

    
risposta data 09.07.2014 - 11:50
fonte
1

Due dei principi più importanti nella progettazione di software gestibili sono KISS e YAGNI .

  • KISS : mantienilo semplice, stupido
  • YAGNI : non ne avrai bisogno

Non è quasi mai una buona idea inserire la logica, non hai immediatamente bisogno di adesso . Tra molte altre persone, Jeff Atwood (un co-fondatore di StackExchange) ha scritto su questo , e nella mia esperienza lui e altri sostenitori di questi concetti hanno perfettamente ragione.

Qualsiasi complessità aggiunta a un programma ha un costo, pagato per un lungo periodo di tempo. Il programma diventa più difficile da leggere, più complesso da modificare e più facile da insinuarsi negli errori. Non cadere nella trappola di aggiungere cose "nel caso in cui". È un falso senso di sicurezza.

Raramente riuscirai a ottenere il codice giusto la prima volta. I cambiamenti sono inevitabili; aggiungendo la logica speculativa alla preparazione difensiva per eventi futuri sconosciuti in realtà non ti proteggerà dal dover rifattorizzare il tuo codice quando il futuro si rivelerà diverso da quello che ti aspetti. Mantenere una logica non necessaria / contingente è più un problema di manutenibilità che un refactoring successivo per aggiungere funzionalità mancanti.

Quindi, poiché sembra che tutto il tuo programma abbia bisogno per ora di sapere se l'operazione è riuscita o meno, la soluzione B proposta (restituisce un singolo booleano) è l'approccio corretto. Puoi sempre refactarlo in seguito se il requisito cambia. Questa soluzione è la più semplice e ha la complessità più bassa (KISS) e fa proprio quello che ti serve e niente di più (YAGNI).

    
risposta data 14.07.2014 - 22:26
fonte
-1

Le righe intere o uno stato di errore

Considera di restituire l'intera riga, almeno come opzione di runtime. Negli inserimenti DB potrebbe essere necessario ispezionare i dati che sono stati inseriti, poiché spesso sarà diverso da ciò che è stato inviato al DB; esempi comuni includono ID di riga generati automaticamente (che l'app avrà probabilmente bisogno immediatamente), valori predefiniti determinati dal DB e risultati di trigger se li si utilizza.

D'altra parte, se non hai bisogno dei dati restituiti, anche il conteggio delle righe interessato non è necessario, poiché non è utile per il risultato di 0. Se ci sono errori, devi restituire che tipo di errore si è verificato, in modo coerente con i principi di gestione degli errori del progetto (eccezioni, codici di errore numerici, qualsiasi cosa); ma esistono query valide che influiscono correttamente su 0 righe (ad esempio "elimina tutti gli ordini scaduti" se in realtà non ce ne sono).

    
risposta data 09.07.2014 - 14:26
fonte

Leggi altre domande sui tag