Mi aspettavo che l'utente API implementasse un UnsupportedOperationException ok?

3

Sto scrivendo un gestore per il download / l'importazione di dati. Dopo aver inserito i dati nel database, è possibile chiamare diversi lavori di importazione, ma solitamente è solo uno. Quindi ci sono due metodi per ottenere i lavori di importazione, che possono essere sostituiti dalla classe base astratta, uno è astratto, l'altro usa il primo.

Dai un'occhiata al mio codice e doc e dimmi se pensi che questo progetto sia ok o quale sia la pratica migliore. Sono particolarmente irritato dal mio design per avere quello che scrive la sottoclasse che deve implementare getImportJob() solo per lanciare un UnsupportedOperationException , in qualche modo è venuto fuori in modo naturale. Ma la parte marcata in grassetto va bene o sto rompendo alcune convenzioni con quella?

/**
 * Returns the name and parameters of the import job that may be called
 * after the data has been added to the database in
 * {@link #insertDataIntoDatabase(Map)}.<br>
 * If the import job is called or not in the aforementioned method is
 * determined by {@link #isExecutingImports()}. If there is no import job to
 * execute ever, this method should return <code>null</code>. If there are
 * several import jobs to be done, this method should throw a
 * {@link UnsupportedOperationException} and {@link #getImportJobList()}
 * should be overridden to return the names and parameters of the import
 * jobs in the order they should be executed.
 * 
 * @return the name of the single import job that can be executed or
 *         <code>null</code> if none can ever be executed.
 * @throws UnsupportedOperationException
 *             If this method was called when there are several import jobs.
 * 
 * @see #getImportJobList()
 */
protected abstract String getImportJob();

/**
 * Returns a list of names and parameters of import jobs that may be called
 * after the data has been added to the database in
 * {@link #insertDataIntoDatabase(Map)}.<br>
 * The default implementation calls {@link #getImportJob()} and returns an
 * empty or a singleton list depending on <code>getImportJob()</code> having
 * returned <code>null</code> or a concrete object. When this method is
 * overridden to provide several import jobs to be done,
 * <code>getImportJob()</code> should also be overriden, according to its
 * documentation.
 * 
 * @return a list of names and parameters of import jobs
 * @throws UnsupportedOperationException
 *             If <code>getImportJob()</code> was overridden to throw an
 *             <code>UnsupportedOperationException</code> without this
 *             method being overridden accordingly.
 * 
 * @see #getImportJob()
 */
public List<String> getImportJobList() {
    String singleImport = getImportJob();
    if (singleImport == null)
        return Collections.emptyList();
    return Collections.singletonList(singleImport);
}

Mi è appena venuto in mente che posso rendere astratti entrambi i metodi e fornire due interfacce, una per singolo e una per multiuso, ognuna delle quali lascia un metodo astratto e implementa l'altro con l'impostazione predefinita in base al contratto ... Quella potrebbe essere un uso improprio del concetto predefinito, ma penso che servirebbe il mio scopo. La mia domanda è ancora valida.

EDIT: Grazie alle risposte ho cambiato la classe base in un'interfaccia senza il metodo single-Job e aggiunto un'interfaccia ereditaria con il metodo single-Job e un'impostazione predefinita per il metodo Job-List .

    
posta GreenThor 09.12.2016 - 16:45
fonte

2 risposte

4

Accademico : se non è necessario implementare una parte dell'interfaccia per soddisfare i requisiti dell'interfaccia, l'interfaccia è mal definita per il tuo caso d'uso. Quello che hai veramente è un'interfaccia diversa.

Quindi: No, non puoi lanciare un UnsupportedOperationException. L'eccezione non dovrebbe nemmeno esistere. "Operazioni non supportate" e "Metodi non implementati" sono chiaramente comunicati in virtù del fatto che non ho codificato l'interfaccia per supportarli. Tali cose sono gomma da masticare e nastro adesivo su disegni scadenti.

Pragmatico : implementare solo parte di un'interfaccia piuttosto che l'intero è sicuramente un odore di codice e introduce il rischio che un consumatore della tua implementazione sperimenti un comportamento che non dovrebbe essere previsto dalla tua interfaccia; usa a tuo rischio e pericolo.

Si dovrebbero progettare i propri sistemi come se tutti i programmatori fossero assassini psicotici dell'ascia che sapessero dove vive il designer. Non saresti la prima persona a fare una cosa del genere, e chiunque abbia esperienza con Win32 probabilmente ti darà un sospiro, uno strappo di occhi e alcuni epiteti borbottati sottovoce. Poi si sarebbero trasferiti. Probabilmente sarai al sicuro.

    
risposta data 12.12.2016 - 16:41
fonte
4

Is expecting the API user to implement an UnsupportedOperationException okay?

Direi di no. Probabilmente stai violando il principio di sostituzione di Liskov . Citando:

No new exceptions should be thrown by methods of the subtype, except where those exceptions are themselves subtypes of exceptions thrown by the methods of the supertype.

quindi, a meno che il tuo metodo di base non sia dichiarato come lancio di una UnsupportedOperationException (o di una superclasse), tale comportamento sarebbe inaspettato e contravverrebbe al principio di minimo stupore .

    
risposta data 09.12.2016 - 17:19
fonte

Leggi altre domande sui tag