Classe astratta vs interfaccia in Java


87

Mi è stata posta una domanda, volevo che la mia risposta fosse rivista qui.

D: In quale scenario è più appropriato estendere una classe astratta piuttosto che implementare le interfacce?

A: Se stiamo usando il modello di progettazione del metodo.

Ho ragione ?

Mi dispiace se non sono stato in grado di formulare chiaramente la domanda.
Conosco la differenza fondamentale tra la classe astratta e l'interfaccia.

1) usa la classe astratta quando il requisito è tale che dobbiamo implementare la stessa funzionalità in ogni sottoclasse per un'operazione specifica (implementare il metodo) e funzionalità diverse per alcune altre operazioni (solo firme del metodo)

2) usa l'interfaccia se devi mettere la firma in modo che sia la stessa (e l'implementazione diversa) in modo da poter essere conforme all'implementazione dell'interfaccia

3) possiamo estendere al massimo una classe astratta, ma possiamo implementare più di un'interfaccia

Ribadendo la domanda: ci sono altri scenari, oltre a quelli sopra menzionati, in cui specificatamente richiediamo di utilizzare la classe astratta (si vede che il modello di progettazione del metodo del modello è concettualmente basato solo su questo)?

Interfaccia vs classe astratta

Scegliere tra questi due dipende davvero da cosa si vuole fare, ma fortunatamente per noi, Erich Gamma può aiutarci un po '.

Come sempre c'è un compromesso, un'interfaccia ti dà la libertà riguardo alla classe base, una classe astratta ti dà la libertà di aggiungere nuovi metodi in seguito . - Erich Gamma

Non puoi cambiare un'interfaccia senza dover cambiare molte altre cose nel tuo codice, quindi l'unico modo per evitarlo sarebbe creare un'interfaccia completamente nuova, che potrebbe non essere sempre una buona cosa.

Abstract classesdovrebbe essere utilizzato principalmente per oggetti strettamente correlati. Interfacessono migliori nel fornire funzionalità comuni per classi non correlate.




Questo non è duplicato. OP vuole sapere quando estendere la classe astratta piuttosto che implementare un'interfaccia. Non vuole sapere quando scrivere una classe o un'interfaccia astratta. La sua classe e interfaccia astratta è già scritta. Hd vuole sapere se estendere o implementare.
Shiplu Mokaddim

1
@ shiplu.mokadd.im Questa è una distinzione senza differenze. Non è possibile utilizzare una classe astratta senza estenderla. Il tuo pignolo qui sembra completamente inutile.
Marchese di Lorne

Risposte:


86

Quando utilizzare le interfacce

Un'interfaccia consente a qualcuno di iniziare da zero per implementare la tua interfaccia o implementare la tua interfaccia in un altro codice il cui scopo originale o principale era abbastanza diverso dalla tua interfaccia. Per loro, la tua interfaccia è solo incidentale, qualcosa che devi aggiungere al loro codice per poter usare il tuo pacchetto. Lo svantaggio è che ogni metodo nell'interfaccia deve essere pubblico. Potresti non voler esporre tutto.

Quando utilizzare le classi astratte

Una classe astratta, al contrario, fornisce più struttura. Di solito definisce alcune implementazioni predefinite e fornisce alcuni strumenti utili per un'implementazione completa. Il problema è che il codice che lo usa deve usare la tua classe come base. Ciò potrebbe essere molto scomodo se gli altri programmatori che desiderano utilizzare il pacchetto hanno già sviluppato la propria gerarchia di classi in modo indipendente. In Java, una classe può ereditare solo da una classe base.

Quando utilizzare entrambi

Puoi offrire il meglio di entrambi i mondi, un'interfaccia e una classe astratta. Gli implementatori possono ignorare la tua classe astratta se lo desiderano. L'unico inconveniente di farlo è chiamare i metodi tramite il nome dell'interfaccia è leggermente più lento che chiamarli tramite il loro nome di classe astratto.


Penso che OP voglia sapere quando estendere la classe astratta piuttosto che implementare un'interfaccia
Shiplu Mokaddim

@ shiplu.mokadd.im In realtà l'OP ha posto una domanda molto specifica, alla quale la risposta è "sì" o "no".
Marchese di Lorne

4
Hai ragione. Ma in SO rispondiamo sì / no con una spiegazione adeguata.
Shiplu Mokaddim

1
@ shiplu.mokadd.im Non vedo come questo ti dia la licenza di esprimere male la sua domanda.
Marchese di Lorne

Solo sulla base di questa singola affermazione If we are using template method design patternnon possiamo dire YESoNO
DivineDesert

31

ribadendo la domanda: c'è qualsiasi altro scenario oltre a quelli sopra menzionati in cui specificatamente richiediamo di usare la classe astratta (si vede che il modello di progettazione del metodo del modello è concettualmente basato solo su questo)

Sì, se usi JAXB. Non gli piacciono le interfacce. Dovresti usare classi astratte o aggirare questa limitazione con i generici.

Da un post del blog personale :

Interfaccia:

  1. Una classe può implementare più interfacce
  2. Un'interfaccia non può fornire alcun codice
  3. Un'interfaccia può definire solo costanti finali statiche pubbliche
  4. Un'interfaccia non può definire variabili di istanza
  5. L'aggiunta di un nuovo metodo ha effetti a catena sull'implementazione delle classi (manutenzione della progettazione)
  6. JAXB non può gestire le interfacce
  7. Un'interfaccia non può estendere o implementare una classe astratta
  8. Tutti i metodi di interfaccia sono pubblici

In generale, le interfacce dovrebbero essere utilizzate per definire i contratti (cosa si deve ottenere, non come ottenerlo).

Classe astratta:

  1. Una classe può estendere al massimo una classe astratta
  2. Una classe astratta può contenere codice
  3. Una classe astratta può definire costanti sia statiche che di istanza (finale)
  4. Una classe astratta può definire variabili di istanza
  5. La modifica del codice di classe astratto esistente ha effetti a catena sull'estensione delle classi (manutenzione dell'implementazione)
  6. L'aggiunta di un nuovo metodo a una classe astratta non ha alcun effetto a catena sull'estensione delle classi
  7. Una classe astratta può implementare un'interfaccia
  8. Le classi astratte possono implementare metodi privati ​​e protetti

Le classi astratte dovrebbero essere utilizzate per l'implementazione (parziale). Possono essere un mezzo per limitare il modo in cui i contratti API dovrebbero essere implementati.


3
In Java 8 per l'interfaccia n. 8, puoi anche avere defaulte staticmetodi.
Utente


9

Ci sono molte ottime risposte qui, ma spesso trovo che usare ENTRAMBE le interfacce e le classi astratte sia il percorso migliore. Considera questo esempio artificioso:

Sei uno sviluppatore di software presso una banca di investimento e devi creare un sistema che inserisca ordini in un mercato. L'interfaccia coglie l'idea più generale di ciò che un sistema di negoziazione fa ,

1) Trading system places orders
2) Trading system receives acknowledgements

e può essere catturato in un'interfaccia, ITradeSystem

public interface ITradeSystem{

     public void placeOrder(IOrder order);
     public void ackOrder(IOrder order);

}

Ora gli ingegneri che lavorano al banco vendite e lungo altre linee di business possono iniziare a interfacciarsi con il tuo sistema per aggiungere funzionalità di posizionamento degli ordini alle loro app esistenti. E non hai ancora iniziato a costruire! Questo è il potere delle interfacce.

Quindi vai avanti e costruisci il sistema per gli operatori di borsa ; hanno sentito che il tuo sistema ha una funzione per trovare azioni a basso costo e sono molto ansiosi di provarlo! Catturi questo comportamento in un metodo chiamato findGoodDeals(), ma realizzi anche che ci sono molte cose disordinate coinvolte nella connessione ai mercati. Ad esempio, devi aprire un SocketChannel,

public class StockTradeSystem implements ITradeSystem{    

    @Override 
    public void placeOrder(IOrder order);
         getMarket().place(order);

    @Override 
    public void ackOrder(IOrder order);
         System.out.println("Order received" + order);    

    private void connectToMarket();
       SocketChannel sock = Socket.open();
       sock.bind(marketAddress); 
       <LOTS MORE MESSY CODE>
    }

    public void findGoodDeals();
       deals = <apply magic wizardry>
       System.out.println("The best stocks to buy are: " + deals);
    }

Le implementazioni concrete avranno molti di questi metodi disordinati come connectToMarket(), ma findGoodDeals()è tutto ciò che interessa ai trader.

Ora è qui che entrano in gioco le classi astratte. Il tuo capo ti informa che anche i trader di valuta vogliono utilizzare il tuo sistema. E guardando i mercati valutari, vedi che l'impianto idraulico è quasi identico ai mercati azionari. In effetti, connectToMarket()può essere riutilizzato alla lettera per connettersi ai mercati dei cambi. Tuttavia, findGoodDeals()è un concetto molto diverso nell'arena valutaria. Quindi, prima di passare il codice base al mago del cambio dall'altra parte dell'oceano, esegui il refactoring in una abstractclasse, lasciandolo non findGoodDeals()implementato

public abstract class ABCTradeSystem implements ITradeSystem{    

    public abstract void findGoodDeals();

    @Override 
    public void placeOrder(IOrder order);
         getMarket().place(order);

    @Override 
    public void ackOrder(IOrder order);
         System.out.println("Order received" + order);    

    private void connectToMarket();
       SocketChannel sock = Socket.open();
       sock.bind(marketAddress); 
       <LOTS MORE MESSY CODE>
    }

Il tuo sistema di trading azionario implementa findGoodDeals()come hai già definito,

public class StockTradeSystem extends ABCTradeSystem{    

    public void findGoodDeals();
       deals = <apply magic wizardry>
       System.out.println("The best stocks to buy are: " + deals);
    }

ma ora il mago del FX può costruire il suo sistema semplicemente fornendo un'implementazione di findGoodDeals()per le valute; non deve reimplementare le connessioni socket o anche i metodi di interfaccia!

public class CurrencyTradeSystem extends ABCTradeSystem{    

    public void findGoodDeals();
       ccys = <Genius stuff to find undervalued currencies>
       System.out.println("The best FX spot rates are: " + ccys);
    }

La programmazione su un'interfaccia è potente, ma applicazioni simili spesso reimplementano metodi in modi quasi identici. L'uso di una classe astratta evita le reimplementazioni, pur preservando la potenza dell'interfaccia.

Nota: ci si potrebbe chiedere perché findGreatDeals()non fa parte dell'interfaccia. Ricorda, l'interfaccia definisce i componenti più generali di un sistema di trading. Un altro ingegnere può sviluppare un sistema di trading COMPLETAMENTE DIVERSO, in cui non si preoccupano di trovare buoni affari. L'interfaccia garantisce che l'ufficio vendite possa interfacciarsi anche al proprio sistema, quindi è preferibile non intrappolare la tua interfaccia con concetti applicativi come "grandi affari".


6

Quale dovresti usare, classi astratte o interfacce?

Prendi in considerazione l'utilizzo di classi astratte se una qualsiasi di queste affermazioni si applica al tuo caso d'uso:

Vuoi condividere il codice tra diverse classi strettamente correlate.

Ti aspetti che le classi che estendono la tua classe astratta abbiano molti metodi o campi comuni o richiedano modificatori di accesso diversi da quelli pubblici (come protected e private).

Vuoi dichiarare campi non statici o non finali. Ciò consente di definire metodi che possono accedere e modificare lo stato dell'oggetto a cui appartengono.

Prendi in considerazione l'utilizzo di interfacce se una qualsiasi di queste affermazioni si applica al tuo caso d'uso:

Ti aspetti che classi non correlate implementino la tua interfaccia. Ad esempio, le interfacce Comparable e Cloneable sono implementate da molte classi non correlate.

Si desidera specificare il comportamento di un particolare tipo di dati, ma non preoccuparsi di chi implementa il suo comportamento.

Si desidera sfruttare l'ereditarietà multipla di tipo.

http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html


4

Le cose sono cambiate molto negli ultimi tre anni con l'aggiunta di nuove funzionalità per interfacciarsi con la versione Java 8.

Dalla pagina della documentazione di Oracle sull'interfaccia:

Un'interfaccia è un tipo di riferimento, simile a una classe, che può contenere solo costanti, firme di metodo, metodi predefiniti, metodi statici e tipi nidificati. I corpi dei metodi esistono solo per metodi predefiniti e metodi statici.

Come hai citato nella tua domanda, la classe astratta è più adatta per il modello del metodo modello in cui devi creare lo scheletro. L'interfaccia non può essere utilizzata qui.

Un'altra considerazione per preferire la classe astratta all'interfaccia:

Non hai implementazione nella classe base e solo le sottoclassi devono definire la propria implementazione. Hai bisogno di una classe astratta invece dell'interfaccia poiché vuoi condividere lo stato con le sottoclassi.

La classe astratta stabilisce "è una" relazione tra classi correlate e l'interfaccia fornisce "ha una" capacità tra classi non correlate .


Per quanto riguarda la seconda parte della sua domanda, che è valida per la maggior parte dei linguaggi di programmazione tra cui Java prima di java-8 rilascio

Come sempre c'è un compromesso, un'interfaccia ti dà la libertà riguardo alla classe base, una classe astratta ti dà la libertà di aggiungere nuovi metodi in seguito. - Erich Gamma

Non puoi cambiare un'interfaccia senza dover cambiare molte altre cose nel tuo codice

Se preferisci che la classe astratta si interfaccia in precedenza con le due considerazioni precedenti, devi ripensare ora poiché i metodi predefiniti hanno aggiunto potenti funzionalità alle interfacce.

I metodi predefiniti consentono di aggiungere nuove funzionalità alle interfacce delle librerie e garantire la compatibilità binaria con il codice scritto per le versioni precedenti di tali interfacce.

Per selezionarne uno tra l'interfaccia e la classe astratta, la pagina della documentazione di Oracle riporta quanto segue:

Le classi astratte sono simili alle interfacce. Non è possibile istanziarli e possono contenere una combinazione di metodi dichiarati con o senza un'implementazione. Tuttavia, con le classi astratte, è possibile dichiarare campi che non sono statici e finali e definire metodi concreti pubblici, protetti e privati.

Con le interfacce, tutti i campi sono automaticamente pubblici, statici e finali e tutti i metodi dichiarati o definiti (come metodi predefiniti) sono pubblici. Inoltre, puoi estendere solo una classe, astratta o meno, mentre puoi implementare un numero qualsiasi di interfacce.

Fare riferimento a queste domande correlate per maggiori dettagli:

Interfaccia vs classe astratta (OO generale)

Come avrei dovuto spiegare la differenza tra un'interfaccia e una classe astratta?

In sintesi: la bilancia si sta orientando maggiormente verso le interfacce ora .

Ci sono altri scenari, oltre a quelli sopra menzionati, in cui specificatamente richiediamo l'uso di una classe astratta (si vede che il modello di progettazione del metodo del modello è concettualmente basato solo su questo)?

Alcuni modelli di progettazione utilizzano classi astratte (su interfacce) oltre al modello di metodo Template.

Modelli creativi:

Abstract_factory_pattern

Modelli strutturali:

Decorator_pattern

Modelli comportamentali:

Mediator_pattern


Questo: "La classe astratta stabilisce" è una "relazione tra classi correlate e l'interfaccia fornisce" ha una "capacità tra classi non correlate".
Gabriel

3

Non hai ragione. Ci sono molti scenari. Semplicemente non è possibile ridurlo a una singola regola di 8 parole.


1
A meno che tu non sia vago come; Usa un'interfaccia ogni volta che puoi;)
Peter Lawrey

@PeterLawrey Sì, non lasciare che gli argomenti circolari ti rallentino ;-)
Marchese di Lorne

Dopo tutto, questo è "overflow dello stack". ;) Il punto è che se puoi usare l'interfaccia più semplice, fallo. Altrimenti non hai altra scelta che usare una classe astratta. Non lo vedo molto complicato.
Peter Lawrey

Penso che tu possa fornire un'idea più costruttiva. come parlare di alcuni scenari rappresentativi /
Adams.H

3

La risposta più breve è, estendi la classe astratta quando alcune delle funzionalità che cerchi sono già implementate in essa.

Se implementi l'interfaccia devi implementare tutto il metodo. Ma per la classe astratta il numero di metodi da implementare potrebbe essere inferiore.

Nel modello di progettazione del modello deve essere definito un comportamento. Questo comportamento dipende da altri metodi astratti. Creando una sottoclasse e definendo questi metodi si definisce effettivamente il comportamento principale. Il comportamento sottostante non può essere in un'interfaccia poiché l'interfaccia non definisce nulla, si limita a dichiarare. Quindi un modello di progettazione del modello viene sempre fornito con una classe astratta. Se vuoi mantenere intatto il flusso del comportamento devi estendere la classe astratta ma non sovrascrivere il comportamento principale.


Ulteriori riferimenti per Pure Virtual Function aggiungeranno ulteriori approfondimenti sulla convergenza di classe astratta e interfaccia , Pure virtual functions can also be used where the method declarations are being used to define an interface - similar to what the interface keyword in Java explicitly specifies. In such a use, derived classes will supply all implementations. In such a design pattern, the abstract class which serves as an interface will contain only pure virtual functions, but no data members or ordinary methods. parte (1/2)
Abhijeet

La parte (2/2) Divergenza di classe astratta e interfaccia è spiegata dall'ultima riga sopra no data members or ordinary methods[in classe astratta].
Abhijeet

3

Secondo me, la differenza fondamentale è questa an interface can't contain non abstract methods while an abstract class can. Quindi se le sottoclassi condividono un comportamento comune, questo comportamento può essere implementato nella super classe e quindi ereditato nelle sottoclassi

Inoltre ho citato quanto segue dal libro "software architecture design ppatterns in java"

"Nel linguaggio di programmazione Java non c'è supporto per l'ereditarietà multipla. Ciò significa che una classe può ereditare solo da una singola classe. Quindi l'ereditarietà dovrebbe essere usata solo quando è assolutamente necessario. Quando possibile, i metodi che denotano il comportamento comune dovrebbero essere dichiarati in la forma di un'interfaccia Java che deve essere implementata da diverse classi di implementatori. Ma le interfacce soffrono della limitazione di non poter fornire implementazioni di metodi. Ciò significa che ogni implementatore di un'interfaccia deve implementare esplicitamente tutti i metodi dichiarati in un'interfaccia, anche quando alcuni di questi i metodi rappresentano la parte invariabile della funzionalità e hanno esattamente la stessa implementazione in tutte le classi dell'implementatore, il che porta a un codice ridondante.Il seguente esempio dimostra come il pattern Abstract Parent Class può essere utilizzato in questi casi senza richiedere implementazioni di metodi ridondanti. "


2

Le classi astratte sono diverse dalle interfacce in due aspetti importanti

  • forniscono un'implementazione predefinita per i metodi scelti (che è coperta dalla tua risposta)
  • le classi astratte possono avere stato (variabili di istanza), quindi questa è un'altra situazione in cui si desidera utilizzarle al posto delle interfacce

Vorrei completare che le interfacce possono avere variabili ma sono per impostazione predefinita finali.
Tomasz Mularczyk

1

Questa è una buona domanda. I due di questi non sono simili ma possono essere utilizzati per alcune delle stesse ragioni, come una riscrittura. Durante la creazione è meglio usare Interface. Quando si tratta di lezioni, è utile per il debug.


0

Abstract classes should be extended when you want to some common behavior to get extended. La super classe Abstract avrà il comportamento comune e definirà il metodo astratto / comportamento specifico che le sottoclassi dovrebbero implementare.

Interfaces allows you to change the implementation anytime allowing the interface to be intact.


0

Questa è la mia comprensione, spero che questo aiuti

Classi astratte:

  1. Può avere variabili membro ereditate (non può essere fatto nelle interfacce)
  2. Può avere costruttori (le interfacce non possono)
  3. I suoi metodi possono avere qualsiasi visibilità (ad esempio: privato, protetto, ecc - mentre tutti i metodi di interfaccia sono pubblici)
  4. Può avere metodi definiti (metodi con un'implementazione)

Interfacce:

  1. Possono avere variabili, ma sono tutte variabili finali statiche pubbliche
    • valori costanti che non cambiano mai con un ambito statico
    • le variabili non statiche richiedono un'istanza e non è possibile creare un'istanza di un'interfaccia
  2. Tutti i metodi sono astratti (nessun codice nei metodi astratti)
    • tutto il codice deve essere effettivamente scritto nella classe che implementa la particolare interfaccia

0

Utilizzo dell'abstract e dell'interfaccia:

Uno ha "È-una-relazione" e un altro ha "Ha-una-relazione"

Le proprietà predefinite sono impostate in astratto e le proprietà extra possono essere espresse tramite l'interfaccia.

Esempio: -> Negli esseri umani abbiamo alcune proprietà predefinite che sono mangiare, dormire, ecc. Ma se qualcuno ha altre attività curriculari come il nuoto, il gioco ecc., Quelle potrebbero essere espresse dall'interfaccia.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.