2010-07-22 8 views
30

Ho letto molto sulle interfacce e l'ereditarietà delle classi in Java, e so come fare entrambe le cose e penso di avere un buon feeling per entrambi. Ma sembra che nessuno parli mai realmente i due fianco a fianco e spiega quando e perché vorresti usare l'uno o l'altro. Non ho trovato molte volte quando implementare un'interfaccia sarebbe un sistema migliore dell'estensione di una superclasse.Quando implementare un'interfaccia e quando estendere una superclasse?

Quindi quando implementate un'interfaccia e quando estendete una superclasse?

+3

Penso che dovresti considerare la composizione anche in questo - che è un concetto importante in termini di riutilizzo - http://www.artima.com/designtechniques/compoinh.html – Jon

risposta

34

Utilizzare un un'interfaccia se si desidera definire un contratto. Cioè X deve prendere Y e restituire Z. Non si cura di come fa il codice. Una classe può implementare più interfacce.

Utilizzare un classe astratta se si vuole definire il comportamento di default a metodi non astratti in modo che gli utenti finali possono riutilizzare senza riscrivere ancora e ancora. Una classe può estendersi da solo una classe. Una classe astratta con i metodi astratti solo può essere definita come un'interfaccia. Una classe astratta senza alcun metodo astratto è riconoscibile come il modello Template Method (vedere this answer per alcuni esempi del mondo reale).

Una classe astratta a sua volta può perfettamente implementare un'interfaccia ogni volta che si desidera fornire all'utente finale la libertà di definire il comportamento predefinito.

+1

Solo un commento secondario su questo, è possibile fornire un comportamento predefinito utilizzando le interfacce, è possibile utilizzare il modello di strategia e disporre di una strategia predefinita su un costruttore della classe (e potenzialmente un altro costruttore che accetta qualsiasi strategia). Penso che una classe astratta abbia più senso quando si vuole fornire un "framework" per gli altri utenti della tua classe a cui attingere, solitamente questo comporta una riflessione o l'uso di diversi metodi ABCDE in un ordine corretto, mentre consente di scavalcare alcuni di quelli. Questo è scomodo da fare con le sole interfacce – JavierIEH

0

Se si desidera ereditare solo le firme del metodo (nome, argomenti, tipo di restituzione) nelle sottoclassi, utilizzare un'interfaccia, ma se si desidera ereditare anche il codice di implementazione, utilizzare una superclasse.

3

Nessuno?

http://mindprod.com/jgloss/interfacevsabstract.html

EDIT: Vorrei fornire più di un collegamento

Ecco una situazione. Per costruire l'esempio auto di seguito, in considerazione questa

interface Drivable { 
    void drive(float miles); 
} 

abstract class Car implements Drivable { 
    float gallonsOfGas; 
    float odometer; 
    final float mpg; 
    protected Car(float mpg) { gallonsOfGas = 0; odometer = 0; this.mpg = mpg; } 
    public void addGas(float gallons) { gallonsOfGas += gallons; } 
    public void drive(float miles) { 
     if(miles/mpg > gallonsOfGas) throw new NotEnoughGasException(); 
     gallonsOfGas -= miles/mpg; 
     odometer += miles; 
    } 
} 

class LeakyCar extends Car { // still implements Drivable because of Car 
    public addGas(float gallons) { super.addGas(gallons * .8); } // leaky tank 
} 

class ElectricCar extends Car { 
    float electricMiles; 
    public void drive(float miles) { // can we drive the whole way electric? 
     if(electricMiles > miles) { 
      electricMiles -= miles; 
      odometer += miles; 
      return;     // early return here 
     } 
     if(electricMiles > 0) { // exhaust electric miles first 
      if((miles-electricMiles)/mpg > gallonsOfGas) 
       throw new NotEnoughGasException(); 
      miles -= electricMiles; 
      odometer += electricMiles; 
      electricMiles = 0; 
     } 
     // finish driving 
     super.drive(miles); 
    } 
} 
+4

Questo articolo sembra distorto rispetto alle interfacce. Per esempio. "Una classe astratta, al contrario, fornisce più struttura.", "[Un'interfaccia è] lenta, richiede un'ulteriore indiretta". Inoltre, non risolve il vero problema con le classi astratte (accoppiamento stretto e codice difficile da leggere). – Sjoerd

+0

+1 per un buon collegamento – naikus

0

immagino ti darò l'esempio di auto d'epoca.

Quando si dispone di un'interfaccia per auto, è possibile creare una Ford, una Chevy e una Oldsmobile. In altre parole, crei diversi tipi di auto dall'interfaccia di un'auto.

Quando si dispone di una classe di auto, è quindi possibile estendere la classe di auto per fare un camion o un autobus. In altre parole, si aggiungono nuovi attributi alle sottoclassi mantenendo gli attributi della base o della super classe.

+0

Direi che Car dovrebbe essere una classe astratta e non un'interfaccia. Ford, Chevy dovrebbe estendere la classe di auto astratta. – Thunderhashy

+0

Stava dicendo che potresti, non che dovresti.La mia regola empirica è "le classi SONO cose, le interfacce fanno cose" - quindi, avrei "Auto astratta che si estende Drivable" come nel mio esempio sopra – corsiKa

+0

Certo, potresti definire Car come una classe astratta. Un'interfaccia è un contratto con diverse possibili implementazioni. –

1

Penso che le interfacce funzionino al meglio quando le si utilizza per esprimere che l'oggetto ha una determinata proprietà o comportamento, che si estende su più alberi di ereditarietà, ed è solo chiaramente definito per ogni classe.

Ad esempio, pensare a Paragonabile. Se si desidera creare una classe Comparable per essere estesa da altre classi, dovrebbe essere molto alta nell'albero di ereditarietà, possibile subito dopo Object, e la proprietà che esprime è che due oggetti di quel tipo possono essere confrontati, ma c'è nessun modo per definirlo in generale (non si può avere un'implementazione di compareTo direttamente nella classe Comparable, è diverso per ogni classe che lo implementa).

Le classi funzionano meglio quando definiscono qualcosa di chiaro, si conoscono le proprietà e i comportamenti che hanno e si hanno implementazioni effettive per i metodi che si vogliono trasmettere ai bambini.

Così le classi funzionano quando è necessario definire un oggetto concreto come un essere umano o un'auto e le interfacce funzionano meglio quando è necessario un comportamento più astratto troppo generico per appartenere a qualsiasi albero di ereditarietà, come la possibilità di essere confrontato (Comparabile) o da eseguire (Runnable).

5

Utilizzare un'interfaccia per definire il comportamento. Classi (e sottoclassi) dell'utente (astratto) per fornire l'implementazione . Non si escludono a vicenda; possono lavorare tutti insieme.

Ad esempio, diciamo che stai definendo un oggetto di accesso ai dati. Vuoi che il tuo DAO sia in grado di caricare i dati. Quindi metti un metodo di caricamento sull'interfaccia. Ciò significa che tutto ciò che vuole chiamarsi DAO deve implementare il carico. Ora diciamo che è necessario caricare A e B. È possibile creare una classe astratta generica che è parametrizzata (generica) per fornire la struttura su come funziona il carico. È quindi sottoclasse che classe astratta per fornire le implementazioni concrete per A e B.

1

Si può pensare di estendere da una super classe se la classe derivata è dello stesso tipo. Voglio dire che quando una classe estende una classe astratta, entrambi dovrebbero essere dello stesso tipo, l'unica differenza è che il super la classe ha un comportamento più generale e la sottoclasse ha un comportamento più specifico. Un'interfaccia è un concetto completamente diverso. Quando una classe implementa un'interfaccia, può esporre qualche API (contratto) o ottenere un determinato comportamento. Per fare un esempio, direi che Car è una classe astratta. È possibile estendere molte classi da esso come dire Ford, Chevy e così via quali sono ciascuno di tipo macchina. Ma se hai bisogno di un comportamento specifico come dire che hai bisogno di un GPS in una macchina, allora la classe concreta, ad esempio Ford, dovrebbe implementare l'interfaccia GPS.

3

Il motivo principale per l'utilizzo di classi e interfacce astratte è diverso.

Una classe astratta deve essere utilizzata quando si dispone di classi che hanno implementazioni identiche per un sacco di metodi, ma variano in pochi.

Questo può essere un cattivo esempio, ma l'uso più ovvio di classi astratte nel framework Java è all'interno delle classi java.io. OutputStream è solo un flusso di byte. Dove va a finire questo flusso dipende interamente dalla sottoclasse di OutputStream che stai utilizzando ... FileOutputStream, PipedOutputStream, lo stream di output creato dal metodo di java.net.Socket ...

Nota: java.io utilizza anche il pattern Decorator per racchiudere stream in altri flussi/lettori/scrittori.

Un'interfaccia dovrebbe essere utilizzata quando si vuole solo garantire che una classe implementa un insieme di metodi, ma non importa come.

L'uso più evidente delle interfacce è all'interno del framework Collections.

Non mi importa come un List aggiunge/rimuove gli elementi, fino a quando posso chiamare add(something) e get(0) mettere e ottenere elementi. Può usare un array (ArrayList, CopyOnWriteArrayList), lista collegata (LinkedList), ecc ...

L'altro vantaggio nell'utilizzo di interfacce è che una classe può realizzare più di uno. LinkedList è un'implementazione di entrambi ListeDeque.

12

si dovrebbe scegliere un un'interfaccia se invece si è interessati ad definire un contratto ossia le firme dei metodi che si vogliono le classi che ereditano da implementare. Un'interfaccia non può avere alcuna implementazione. Le classi ereditanti sono libere di scegliere la propria implementazione.

A volte si desidera definire l'implementazione parziale in un tipo di base e si desidera lasciare il resto alle classi ereditanti. In questo caso, scegli una classe astratta . Una classe astratta può definire implementazioni e variabili del metodo lasciando alcuni metodi astratti. L'estensione delle classi può scegliere come implementare i metodi astratti mentre hanno anche l'implementazione parziale fornita dalla superclasse.

Un estremo delle classi astratte è una pura classe astratta, una che ha solo metodi astratti e nient'altro. Se si tratta della classe astratta pura rispetto a un'interfaccia, andare con l'interfaccia. Java consente solo l'ereditarietà di un'implementazione singola mentre consente l'ereditarietà di un'interfaccia multipla nel senso che una classe può implementare più interfacce ma può estendere solo una classe. Quindi scegliere una classe astratta pura attraverso l'interfaccia significa che la sottoclasse non sarà in grado di estendere qualsiasi altra classe mentre implementa i metodi astratti.

2

Un metodo di scelta tra uno interface e un base class è l'esame della proprietà del codice. Se controlli tutto il codice, una classe base è un'opzione valida. Se d'altra parte molte aziende diverse potrebbero voler produrre componenti sostituibili, ovvero definire un contratto allora un'interfaccia è la tua unica scelta.

Problemi correlati