La gestione delle eccezioni viola il "programma di astrazione"? [duplicare]

3

Sto parlando in base all'esperienza con Java e C #. Non so se un'altra lingua abbia un'implementazione di gestione delle eccezioni diversa.

Per ottenere un accoppiamento lento, abbiamo bisogno che il nostro codice sia programmato per usare l'astrazione piuttosto che l'implementazione. Tuttavia, il caso di gestione delle eccezioni è il contrario. La best practice è che devi gestire un tipo di eccezione specifico ( SqlException , StackOverflowException , ecc.)

Questa cosa potrebbe essere migliore (o meno) in java grazie per il suo Checked Exception , c'è una specie di "contratto" tra l'interfaccia e il consumatore. Ma in C # o per Unchecked Exception non c'è un contratto su quale eccezione può essere lanciata dall'interfaccia.

Ad esempio, supponiamo di utilizzare Repository Pattern per disaccoppiare DAL con BLL. L'eccezione di cattura semplice di solito si usa come:

public void Consume()
{
    try{
        productRepository.Get(k=>k.Id == "0001");
    }
    catch(Exception e){
        // handle
    }
}

Nel caso più specifico usiamo solitamente SqlException . Tuttavia significa che dobbiamo sapere che ProductRepository è un repository sul server di database. Cosa succede se l'implementazione è stata modificata per utilizzare invece il repository di file? Ora devi prendere FileNotFoundException o qualcosa del genere.

Perché viola il principio del "codice per l'astrazione"? E cosa possiamo fare per impedirlo?

    
posta Fendy 16.01.2014 - 06:22
fonte

3 risposte

7

Dovresti gestire un caso specifico dove è appropriato per gestire quel caso specifico.

Nel tuo esempio, il consumatore non è il livello di astrazione appropriato per gestire una SqlException, come hai ipotizzato.

Il repository stesso, che sa di essere recuperato da un database, dovrebbe essere quello che gestisce SqlException, magari riorganizzandolo in una DataAccessException più specifica per l'applicazione e personalizzata che può essere gestita in modo appropriato dal consumatore.

Se si modifica l'implementazione del repository per estrarre dati da, ad esempio, un servizio Web, tale repository può gestire un'HttpException e riconfezionarla in DataAccessException. Oppure un repository XML può gestire eccezioni specifiche per XML e riconfezionarle. E così via.

    
risposta data 16.01.2014 - 06:39
fonte
1

Dipende molto dal tipo di eccezione di cui stai parlando. Le eccezioni devono essere eccezionali, ma altre no.

Ad esempio, diciamo che il metodo di interfaccia IProductRepo.Get ha una condizione implicita, che restituisce sempre un prodotto. Ma per qualche ragione, l'implementazione ProductRepo di esso può generare un'eccezione se il prodotto non esiste. In questo caso puoi gestire l'eccezione in Consume e agire in modo appropriato nel caso in cui il prodotto non venga trovato. Ma come supponete, questa è una violazione dell'astrazione, più correttamente una violazione di LSP come discusso qui . Questo di solito implica che la tua astrazione è sbagliata e dovresti rimodellare per catturare correttamente il caso aggiuntivo. In questo esempio, potresti dire che il metodo Get potrebbe restituire null se il prodotto non viene trovato. Questo tipo di eccezione può verificarsi anche se hai alcune affermazioni nel tuo codice e falliscono. Ma questo di solito significa bug nel tuo codice da qualche parte.

Dall'altro lato, ci sono eccezioni o eccezioni veramente eccezionali che la tua applicazione non può prevedere. Ad esempio, perdere una connessione al DB è eccezionale. La cosa con questo tipo di eccezioni è che non possono essere gestiti correttamente all'interno della logica dell'applicazione. Ad esempio, se l'hai gestito come nel tuo esempio, equivaresti a non trovare un prodotto e perdere una connessione dicendo che è lo stesso. Che è ovviamente sbagliato. Questo genere di eccezioni può essere gestito solo in modo generico sul confine del programma. In caso di applicazione dell'interfaccia utente, tutto ciò che si può fare è visualizzare "L'operazione non può essere completata perché: (motivo qui). Riprova più tardi." e spero che l'utente non lo riempia come una segnalazione di bug. Non vi è alcuna violazione qui, perché non estende i possibili casi che il chiamante deve gestire. Quindi non estende il contratto.

    
risposta data 16.01.2014 - 09:01
fonte
-1

Ma le eccezioni fanno parte del contratto!

Devi definire quali eccezioni devono essere gestite nell'interfaccia.

Proprio come il leasing di noleggio potrebbe specificare "devi assicurarti contro i danni da incendio", quindi un'interfaccia può specificare che devi gestire SqlException.

In Java almeno il compilatore dà un errore se un'implementazione genera un'eccezione non menzionata esplicitamente nell'interfaccia.

Questo non vuol dire che le eccezioni dovrebbero essere passate ciecamente al genitore. Per quanto possibile, le eccezioni dovrebbero essere gestite all'interno di qualsiasi implementazione, ma a volte questo non è possibile. Un'implementazione non può nascondere il fatto che non può connettersi alla sua origine dati sia che si tratti di un database, di un servizio Web o altro.

    
risposta data 16.01.2014 - 09:18
fonte