Domanda

Di recente ho partecipato a un'intervista e mi hanno posto la domanda "Perché le interfacce sono preferite rispetto alle lezioni astratte?"

Ho provato a dare alcune risposte come:

  • Siamo in grado di ottenere una sola funzionalità di estensione
  • sono astratti al 100%
  • L'implementazione non è hardcoded

Mi hanno chiesto di prendere una delle API JDBC che usi. " Perché sono interfacce? " ;.

Posso ottenere una risposta migliore per questo?

È stato utile?

Soluzione

Quella domanda di intervista riflette una certa convinzione della persona che la pone. Credo che la persona abbia torto, e quindi puoi seguire una delle due direzioni.

  1. Dai loro la risposta che vogliono.
  2. Rispettosamente in disaccordo.

La risposta che vogliono, beh, gli altri poster hanno evidenziato quelli incredibilmente bene. Eredità di interfaccia multipla, l'ereditarietà costringe la classe a fare scelte di implementazione, le interfacce possono essere modificate più facilmente.

Tuttavia, se si crea un argomento convincente (e corretto) nel proprio disaccordo, l'intervistatore potrebbe prenderne atto. Innanzitutto, evidenzia le cose positive sulle interfacce, questo è un must. In secondo luogo, direi che le interfacce sono migliori in molti scenari, ma portano anche alla duplicazione del codice che è una cosa negativa. Se hai una vasta gamma di sottoclassi che eseguiranno in gran parte la stessa implementazione, oltre a funzionalità extra, potresti voler una classe astratta. Ti permette di avere molti oggetti simili con dettagli dettagliati, mentre con solo interfacce, devi avere molti oggetti distinti con un codice quasi duplicato.

Le interfacce hanno molti usi e c'è una ragione convincente per credere che siano "migliori". Tuttavia, dovresti sempre utilizzare lo strumento corretto per il lavoro e ciò significa che non puoi cancellare le classi astratte.

Altri suggerimenti

In generale, e questa non è affatto una "regola". che dovrebbe essere seguito alla cieca, la disposizione più flessibile è:

interface
   abstract class
       concrete class 1       
       concrete class 2

L'interfaccia è lì per un paio di motivi:

  • una classe esistente che già estende qualcosa può implementare l'interfaccia (supponendo che tu abbia il controllo sul codice per la classe esistente)
  • una classe esistente può essere sottoclassi e la sottoclasse può implementare l'interfaccia (supponendo che la classe esistente sia sottoclassabile)

Ciò significa che puoi prendere classi preesistenti (o solo classi che DEVONO estendersi da qualcos'altro) e farle funzionare con il tuo codice.

La classe astratta è lì per fornire tutti i bit comuni per le classi concrete. La classe astratta viene estesa quando si scrivono nuove classi o si modificano le classi che si desidera estendere (supponendo che si estendano da java.lang.Object).

Dovresti sempre (a meno che tu non abbia una buona ragione per non) dichiarare le variabili (parametri di istanza, classe, locale e metodo) come interfaccia.

Ottieni solo un colpo in eredità. Se crei una classe astratta anziché un'interfaccia, qualcuno che eredita la tua classe non può anche ereditare una diversa classe astratta.

Puoi implementare più di un'interfaccia, ma puoi ereditare solo da una singola classe

Classi astratte

1.Non può essere istanziato indipendentemente dalle loro classi derivate. I costruttori di classi astratte sono chiamati solo dalle loro classi derivate.

2. Definire le firme dei membri astratti che le classi base devono implementare.

3.Sono più estensibili delle interfacce, senza interrompere alcuna compatibilità di versione. Con le classi astratte, è possibile aggiungere ulteriori membri non astratti che possono ereditare tutte le classi derivate.

4.Può includere dati memorizzati nei campi.

5. Consentire ai membri (virtuali) che hanno un'implementazione e, quindi, forniscono un'implementazione predefinita di un membro alla classe derivante.

6. La derivazione da una classe astratta utilizza la sola e unica opzione della classe base di una sottoclasse.

Interfaccia

1.Non può essere istanziato.

2. L'implementazione di tutti i membri dell'interfaccia avviene nella classe base. Non è possibile implementare solo alcuni membri all'interno della classe di implementazione.

3. L'estensione delle interfacce con membri aggiuntivi interrompe la compatibilità della versione.

4.Non è possibile archiviare alcun dato. I campi possono essere specificati solo nelle classi derivate. La soluzione alternativa è quella di definire le proprietà, ma senza implementazione.

5.Tutti i membri sono automaticamente virtuali e non possono includere alcuna implementazione.

6. Sebbene non possa apparire alcuna implementazione predefinita, le classi che implementano le interfacce possono continuare a derivare l'una dall'altra.

Come devinb e altri citano, esso sembra che l'intervistatore mostri la propria ignoranza nel non accettare le risposte valide.

Tuttavia, la menzione di JDBC potrebbe essere un suggerimento. In tal caso, forse stanno chiedendo i vantaggi di una client codifica rispetto a un'interfaccia anziché a una classe.

Quindi, invece di risposte perfettamente valide come " ottieni solo un uso dell'ereditarietà " , che si riferiscono al design di classe, potrebbero cercare una risposta più simile a " disaccoppia un client da un'implementazione specifica " .

Le classi astratte hanno una serie di potenziali insidie. Ad esempio, se si ignora un metodo, il metodo super () non viene chiamato a meno che non venga chiamato esplicitamente. Ciò può causare problemi per le classi di override mal implementate. Inoltre, ci sono potenziali problemi con equals () quando si utilizza l'ereditarietà.

L'uso delle interfacce può incoraggiare l'uso della composizione quando si desidera condividere un'implementazione. La composizione è molto spesso un modo migliore per riutilizzare gli altri oggetti, poiché è meno fragile. L'ereditarietà è facilmente abusata o utilizzata per scopi sbagliati.

La definizione di un'interfaccia è un modo molto sicuro per definire il modo in cui un oggetto dovrebbe agire, senza rischiare la fragilità che può con l'estensione di un'altra classe, astratta o no.

Inoltre, come dici tu, puoi estendere solo una classe alla volta, ma puoi implementare tutte le interfacce che desideri.

Le classi astratte vengono utilizzate quando si eredita la implementazione , le interfacce vengono utilizzate quando si eredita la specifica . Gli standard JDBC stabiliscono che "una connessione deve fare questo ". Questa è specifica.

Quando usi le classi astratte crei un accoppiamento tra la sottoclasse e la classe base. Questo accoppiamento a volte può rendere il codice davvero difficile da modificare, specialmente all'aumentare del numero di sottoclassi. Le interfacce non presentano questo problema.

Hai anche una sola eredità, quindi dovresti assicurarti di usarla per i motivi giusti.

  

" Perché le interfacce sono preferite rispetto   Classi astratte? & Quot;

Gli altri post hanno fatto un ottimo lavoro nel guardare le differenze tra interfacce e classi astratte, quindi non duplicherò quei pensieri.

Ma guardando la domanda dell'intervista, la domanda migliore è davvero " Quando quando le interfacce dovrebbero essere preferite rispetto alle classi astratte? " (e viceversa).

Come per la maggior parte dei costrutti di programmazione, sono disponibili per una ragione e le dichiarazioni assolute come quella nella domanda dell'intervista tendono a mancare. In un certo senso mi ricorda tutte le affermazioni che hai letto riguardo all'istruzione goto in C. " Non dovresti mai usare goto - rivela scarse capacità di codifica. & Quot; Tuttavia, goto ha sempre avuto i suoi usi appropriati.

Rispettosamente in disaccordo con la maggior parte dei poster di cui sopra (mi dispiace! Modificami se vuoi :-))


Innanzitutto, la "sola super classe" la risposta è pessima. Chiunque mi avesse dato quella risposta in un'intervista sarebbe stato rapidamente contrastato con "C ++ esisteva prima che Java e C ++ avessero più superclassi. Perché pensi che James Gosling abbia permesso solo una superclasse per Java? & Quot;

Comprendi la filosofia alla base della tua risposta, altrimenti sei un brindisi (almeno se ti intervisto.)


In secondo luogo, le interfacce hanno molteplici vantaggi rispetto alle classi astratte, specialmente quando si progettano interfacce. Il più grande non ha una particolare struttura di classe imposta al chiamante di un metodo. Non c'è niente di peggio che provare a usare una chiamata di metodo che richiede una particolare struttura di classe. È doloroso e imbarazzante. Utilizzando un'interfaccia nulla può essere passato al metodo con un minimo di aspettative.

Esempio:

public void foo(Hashtable bar);

vs.

public void foo(Map bar);

Per il primo, il chiamante prenderà sempre la propria struttura di dati esistente e la sbatterà in una nuova Hashtable.


In terzo luogo, le interfacce consentono ai metodi pubblici negli implementatori di classi concrete di essere "privati". Se il metodo non è dichiarato nell'interfaccia, il metodo non può essere utilizzato (o utilizzato in modo improprio) da classi che non hanno attività commerciali utilizzando il metodo. Il che mi porta al punto 4 ....


In quarto luogo, le interfacce rappresentano un contratto minimo tra la classe di implementazione e il chiamante. Questo contratto minimo specifica esattamente come l'implementatore concreto prevede di essere utilizzato e non di più. Alla classe chiamante non è consentito utilizzare nessun altro metodo non specificato dal "contratto" dell'interfaccia. Il nome dell'interfaccia in uso assapora anche le aspettative dello sviluppatore su come dovrebbero usare l'oggetto. Se uno sviluppatore viene passato a

public interface FragmentVisitor {
    public void visit(Node node);
}

Lo sviluppatore sa che l'unico metodo che può chiamare è il metodo visit. Non vengono distratti dai metodi brillanti e brillanti nella classe concreta con cui non dovrebbero scherzare.


Infine, le classi astratte hanno molti metodi che sono realmente presenti solo per le sottoclassi da usare. Quindi le classi astratte tendono a sembrare un po 'come un disastro per lo sviluppatore esterno, non ci sono indicazioni su quali metodi debbano essere usati dal codice esterno.

Sì, certo, alcuni di questi metodi possono essere protetti. Tuttavia, metodi purtroppo protetti sono visibili anche ad altre classi nello stesso pacchetto. E se il metodo di una classe astratta implementa un'interfaccia, il metodo deve essere pubblico.

Tuttavia, usando le interfacce tutte queste viscere che si trovano quando si guarda la superclasse astratta o la classe concreta sono nascoste in modo sicuro.


Sì, lo so che ovviamente lo sviluppatore può usare un po 'di "speciale" conoscenza per lanciare un oggetto su un'altra interfaccia più ampia o sulla stessa classe concreta. Ma un cast del genere viola il contratto previsto e lo sviluppatore dovrebbe essere schiaffeggiato con un salmone.

Se pensano che X sia migliore di Y non sarei preoccupato di ottenere il lavoro, non mi piacerebbe lavorare per qualcuno che mi ha costretto a progettare su un altro perché mi è stato detto che le interfacce sono le migliori. Entrambi sono buoni a seconda della situazione, altrimenti perché la lingua ha scelto di aggiungere classi astratte? Sicuramente, i progettisti del linguaggio sono più intelligenti di me.

Questo è il problema di "Eredità multipla". Possiamo " estendere " non più di una classe abstarct alla volta attraverso un'altra classe ma in Interfaces, possiamo "implementare" interfacce multiple in singola classe. Pertanto, sebbene Java non fornisca ereditarietà multipla in generale, ma utilizzando le interfacce possiamo incorporare in essa proprietà dell'ereditarietà multiplt.

Spero che questo aiuti !!!

interface è un modo più pulito di scrivere una classe puramente astratta. Puoi dire che l'implementazione non si è intrufolata (ovviamente potresti volerlo fare in certe fasi di manutenzione, il che rende le interfacce cattive). Questo è tutto. Non c'è quasi alcuna differenza distinguibile dal codice client.

JDBC è un esempio davvero negativo. Chiedi a chiunque abbia tentato di implementare le interfacce e mantenere il codice tra le versioni di JDK. JAX-WS è anche peggio, aggiungendo metodi nelle versioni di aggiornamento.

Esistono differenze tecniche, come la possibilità di moltiplicare "ereditare" interfaccia. Quello tende ad essere il risultato di un design confuso. In rari casi potrebbe essere utile disporre di una gerarchia di implementazione diversa dalla gerarchia dell'interfaccia.

Sul lato negativo delle interfacce, il compilatore non è in grado di raccogliere alcuni cast / istanza di s impossibili.

C'è un motivo non menzionato da quanto sopra.

Puoi decorare facilmente qualsiasi interfaccia con java.lang.reflect.Proxy che ti consente di aggiungere codice personalizzato in fase di esecuzione a qualsiasi metodo nell'interfaccia data. È molto potente.

Vedi http://tutorials.jenkov.com/java-reflection/ dynamic-proxies.html per un tutorial.

La

interfaccia non sostituisce la classe astratta .

Preferisco

interfaccia: per implementare un contratto da più oggetti non correlati

classe astratta: per implementare lo stesso o diverso comportamento tra più oggetti correlati


Fare riferimento a questa domanda SE correlata per casi d'uso sia di interfaccia che di classe astratta

Interface vs Abstract Class (OO generale)

Caso d'uso:

Se devi usare Template_method , non puoi ottenere con l'interfaccia. Classe astratta dovrebbe essere scelta per raggiungerlo.

Se devi implementare una capacità per molti oggetti senza cornice, la classe astratta non serve allo scopo e devi scegliere interfaccia .

Puoi implementare più interfacce, ma in particolare con c # non puoi avere più eredità

Perché le interfacce non ti stanno costringendo in qualche gerarchia ereditaria.

Definisci le interfacce quando richiedi che alcuni oggetti implementino determinati metodi ma non ti interessa il suo pedigree. Quindi qualcuno può estendere una classe esistente per implementare un'interfaccia, senza influire sul comportamento precedentemente esistente di quella classe.

Ecco perché JDBC è tutte le interfacce; non ti interessa davvero quali classi vengono utilizzate in un'implementazione JDBC, hai solo bisogno di qualsiasi implementazione JDBC per avere lo stesso comportamento previsto. Internamente, il driver Oracle JDBC potrebbe essere molto diverso dal driver PostgreSQL, ma questo è irrilevante per te. Uno potrebbe dover ereditare da alcune classi interne già sviluppate dagli sviluppatori del database, mentre un altro potrebbe essere completamente sviluppato da zero, ma non è importante per te fintanto che implementano entrambe le stesse interfacce in modo da poter comunicare con una o la altro senza conoscere il funzionamento interno di nessuno dei due.

Bene, suggerirei che la domanda stessa debba essere riformulata. Le interfacce sono principalmente contratti che una classe acquisisce, l'implementazione del contratto stesso varierà. Una classe astratta conterrà di solito una logica predefinita e le sue classi figlio aggiungeranno altra logica. Direi che la risposta alle domande si basa sul problema dei diamanti. Java impedisce l'ereditarietà multipla per evitarlo. ( http://en.wikipedia.org/wiki/Diamond_problem ).

  

Mi hanno chiesto di prendere una delle API JDBC   che usi. " Perché lo sono   Interfacce "?.

La mia risposta a questa domanda specifica è:

SUN non sa come implementarli o cosa mettere nell'implementazione. Spetta ai fornitori di servizi / fornitori di database mettere la loro logica nell'implementazione.

Il design di JDBC ha una relazione con il modello Bridge, che dice "Disaccoppia un'astrazione dalla sua implementazione in modo che i due possano variare indipendentemente".

Ciò significa che la gerarchia delle interfacce di api JDBC può essere evoluta indipendentemente dalla gerarchia di implementazione fornita o utilizzata da un fornitore jdbc.

Le classi astratte offrono un modo per definire un modello di comportamento, in cui l'utente inserisce i dettagli nei dettagli.

Un buon esempio è di SwingWorker . Definisce un framework per fare qualcosa in background, che richiede all'utente di definire doInBackground () per l'attività effettiva.

Ho esteso questa classe in modo tale da creare automaticamente una barra di avanzamento popup. Ho ignorato done (), per controllare lo smaltimento di questo pop-up, ma poi ho fornito un nuovo punto di override, consentendo all'utente di definire facoltativamente cosa succede dopo che la barra di avanzamento scompare.

public abstract class ProgressiveSwingWorker<T, V> extends SwingWorker<T, V> {

    private JFrame progress;

    public ProgressiveSwingWorker(final String title, final String label) {
        SwingUtilities.invokeLater(new Runnable() {
            @SuppressWarnings("serial")
            @Override
            public void run() {
                progress = new JFrame() {{
                    setLayout(new MigLayout("","[grow]"));
                    setTitle(title);
                    add(new JLabel(label));
                    JProgressBar bar = new JProgressBar();
                    bar.setIndeterminate(true);
                    add(bar);
                    pack();
                    setLocationRelativeTo(null);
                    setVisible(true);
                }};
            }
        });
    }

    /**
     * This method has been marked final to secure disposing of the progress dialog. Any behavior
     * intended for this should be put in afterProgressBarDisposed.
     */
    @Override
    protected final void done() {
        progress.dispose();
        try {
            afterProgressBarDisposed(get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    protected void afterProgressBarDisposed(T results) {
    }

}

L'utente ha ancora l'obbligo di fornire l'implementazione di doInBackground () . Tuttavia, possono anche avere un comportamento di follow-up, come aprire un'altra finestra, visualizzare un JOptionPane con risultati o semplicemente non fare nulla.

Per usarlo:

new ProgressiveSwingWorker<DataResultType, Object>("Editing some data", "Editing " + data.getSource()) {

    @Override
    protected DataResultType doInBackground() throws Exception {
        return retrieve(data.getSource());
    }

    @Override
    protected void afterProgressBarDisposed(DataResultType results) {
        new DataEditor(results);
    }

}.execute();

Questo mostra come una classe astratta può fornire con successo un'operazione basata su modelli, ortogonale al concetto di interfacce che definiscono un contratto API.

Dipende dalle tue esigenze e dal potere di implementazione, che è molto importante. Hai così tante risposte riguardo a questa domanda. Quello che penso a questa domanda è che la classe astratta è l'evoluzione se API. Puoi definire la tua futura definizione di funzione in una classe astratta ma non hai bisogno di tutta l'implementazione della funzione nella tua classe principale ma con l'interfaccia non puoi fare questa cosa.

scroll top