Perché utilizzare le interfacce, l’ereditarietà multipla e le interfacce, i vantaggi delle interfacce?

Ho ancora un po ‘di confusione su questa cosa. Quello che ho trovato fino ad ora è

(Domande simili sono già state poste qui, ma stavo avendo altri punti.)

  1. L’interfaccia è raccolta di SOLO metodi astratti e campi finali.

  2. Non c’è eredità multipla in Java.

  3. Le interfacce possono essere utilizzate per ottenere l’ereditarietà multipla in Java.

  4. Un punto di forza dell’ereditarietà è che possiamo usare il codice della class base nella class derivata senza scriverlo di nuovo. Potrebbe essere questa la cosa più importante per l’eredità di essere lì.

Adesso..

Q1. Poiché le interfacce hanno solo metodi astratti (nessun codice), quindi come possiamo dire che se implementiamo un’interfaccia, allora è ereditaria? Non stiamo usando il suo codice.

Q2. Se l’implementazione di un’interfaccia non è un’eredità, come vengono utilizzate le interfacce per ottenere l’ereditarietà multipla?

Q3. In ogni caso, qual è il vantaggio di utilizzare le interfacce? Non stanno avendo alcun codice. Abbiamo bisogno di scrivere codice ancora e ancora in tutte le classi che implementiamo.

Allora perché creare interfacce?

NOTA: ho trovato un caso in cui le interfacce sono utili. Un esempio di questo è come in Runnable abbiamo il metodo public void run () in cui definiamo la funzionalità del thread e nella codifica è incorporato che questo metodo verrà eseguito come thread separato. Quindi abbiamo solo bisogno di codificare cosa fare nel thread, Rest è predefinito. Ma anche questa cosa può essere raggiunta usando classi astratte e tutto il resto.

Allora quali sono i benefici esatti dell’uso di interfacce? E ‘davvero l’ereditarietà multipla che otteniamo usando le interfacce?

Q1. Poiché le interfacce hanno solo metodi astratti (nessun codice), quindi come possiamo dire che se implementiamo un’interfaccia, allora è ereditaria? Non stiamo usando il suo codice.

Non possiamo. Le interfacce non vengono utilizzate per ottenere eredità multiple. Lo sostituiscono con un costrutto più sicuro, anche se leggermente meno potente. Nota la parola chiave implements piuttosto che extends .

Q2. Se l’implementazione di un’interfaccia non è un’eredità, come vengono utilizzate le interfacce per ottenere l’ereditarietà multipla?

Non sono. Con le interfacce una singola class può avere diverse ” viste “, diverse API o funzionalità. Ad esempio, una class può essere Runnable e Callable allo stesso tempo, mentre entrambi i metodi fanno effettivamente la stessa cosa.

Q3. In ogni caso, qual è il vantaggio di utilizzare le interfacce? Non stanno avendo alcun codice. Abbiamo bisogno di scrivere codice ancora e ancora in tutte le classi che implementiamo.

Le interfacce sono di tipo multiplo ereditario senza problemi introdotte da quest’ultimo (come il problema Diamond ).

Esistono pochi casi d’uso per le interfacce:

  1. L’object ha effettivamente due id quadro: un Tank è sia un Vehicle che Weapon . È ansible utilizzare un’istanza di Tank cui è previsto il primo o il secondo (polimorfismo). Questo è raramente un caso nella vita reale ed è in realtà un esempio valido in cui l’ereditarietà multipla sarebbe migliore (o tratti).

  2. Semplici responsabilità: un’istanza di object Tank in un gioco è Runnable che consente di eseguirla in un thread e ActionListener per rispondere agli eventi del mouse.

  3. Interfacce di callback: se l’object implementa l’interfaccia di callback, viene informato del suo ciclo di vita o di altri eventi.

  4. Interfacce marcatore: non aggiungendo alcun metodo, ma facilmente accessibile tramite instanceof per scoprire funzionalità o desideri dell’object. Serializable e Cloneable sono esempi di questo.

Quello che stai cercando è tratto (come in Scala), sfortunatamente non disponibile in Java.

Le interfacce sono raccolte di campi statici finali e metodi astratti (Newly Java 8 ha aggiunto il supporto di avere metodi statici in un’interfaccia).

Le interfacce sono fatte in situazioni in cui sappiamo che alcune attività devono essere fatte, ma come può essere fatto può variare. In altre parole, possiamo dire che implementiamo interfacce in modo che la nostra class inizi a comportarsi in un modo particolare.

Lasciatemi spiegare con un esempio, sappiamo tutti cosa sono gli animali. Come il Leone è un animale, la scimmia è un animale, l’elefante è un animale, la mucca è un animale e così via. Ora sappiamo che tutti gli animali mangiano qualcosa e dormono. Ma il modo in cui ogni animale può mangiare qualcosa o dormire può essere diverso. Come il Leone mangia cacciando altri animali dove la mucca mangia l’erba. Ma entrambi mangiano. Quindi possiamo avere qualche pseudo-codice come questo,

 interface Animal { public void eat(); public void sleep(); } class Lion implements Animal { public void eat() { // Lion's way to eat } public void sleep(){ // Lion's way to sleep } } class Monkey implements Animal { public void eat() { // Monkey's way to eat } public void sleep() { // Monkey's way to sleep } } 

Come per lo pseudo codice sopra menzionato, tutto ciò che è capace di mangiare o dormire sarà chiamato un animale o possiamo dire che è obbligatorio per tutti gli animali mangiare e dormire, ma il modo di mangiare e dormire dipende dall’animale.

In caso di interfacce ereditiamo solo il comportamento, non il codice reale come nel caso dell’ereditarietà delle classi.

Q1. Poiché le interfacce hanno solo metodi astratti (nessun codice), quindi come possiamo dire che se implementiamo un’interfaccia, allora è ereditaria? Non stiamo usando il suo codice.

Implementare interfacce è un altro tipo di ereditarietà. Non è simile all’ereditarietà delle classi, poiché in quell’ereditarietà la class figlio ottiene il vero codice da riutilizzare dalla class base.

Q2. Se l’implementazione di un’interfaccia non è un’eredità, come vengono utilizzate le interfacce per ottenere l’ereditarietà multipla?

Si dice perché una class può implementare più di una interfaccia. Ma dobbiamo capire che questa eredità è diversa dall’ereditarietà delle classi.

Q3. In ogni caso, qual è il vantaggio di utilizzare le interfacce? Non stanno avendo alcun codice. Abbiamo bisogno di scrivere codice ancora e ancora in tutte le classi che implementiamo.

L’implementazione di un’interfaccia attribuisce alla class la necessità di ignorare tutti i suoi metodi astratti.

Leggi di più nel mio libro qui e qui

BACIO

Ho cercato per giorni, anzi settimane cercando di capire le interfacce e sembra leggere lo stesso aiuto generico; Non sto cercando di denigrare i contributi, ma penso che la lampadina sia scattata così sono sbuffato :))

Preferisco mantenerlo stupido, quindi offrirò la mia nuova vista delle interfacce.

Sono un programmatore casuale ma voglio pubblicare questo codice che ho scritto in VB.NET (il principio è lo stesso per altri linguaggi), per aiutare gli altri a capire le interfacce.

Se ho sbagliato, per favore lascia che altri sappiano nei commenti di follow-up.

Spiegazione

Tre pulsanti su un modulo, facendo clic su ognuno di essi, viene salvato un riferimento di class diverso alla variabile di interfaccia (_data). L’intero punto di diversi riferimenti di class in una variabile di interfaccia, è ciò che non ho capito in quanto sembrava ridondante, quindi il suo potere diventa evidente con msgbox, ho solo bisogno di chiamare il metodo SAME per eseguire l’attività di cui ho bisogno, in questo case ‘GetData ()’, che utilizza il metodo nella class attualmente detenuta dalla variabile di riferimento dell’interfaccia (_data).

Quindi, tuttavia, desidero ottenere i miei dati (da un database, dal web o da un file di testo), è sempre stato fatto usando lo stesso nome di metodo ; il codice dietro questa implementazione … non mi interessa.

È quindi facile cambiare ogni codice di class utilizzando l’interfaccia senza alcuna dipendenza … questo è un objective chiave in OO e incapsulamento.

Quando usare

Classi di codice e se si nota lo stesso verbo utilizzato per i metodi, come ‘GetData ()’, allora è un buon candidato implementare un’interfaccia su quella class e usare quel nome di metodo come un’astrazione / interfaccia.

Spero sinceramente che questo aiuti un noob con questo principio difficile.

 Public Class Form1 Private _data As IData = Nothing Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click _data = New DataText() MsgBox(_data.GetData()) End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click _data = New DataDB() MsgBox(_data.GetData()) End Sub Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click _data = New DataWeb() MsgBox(_data.GetData()) End Sub End Class Public Interface IData Function GetData() As String End Interface Friend Class DataText : Implements IData Friend Function GetData() As String Implements IData.GetData Return "DataText" End Function End Class Friend Class DataDB : Implements IData Friend Function GetData() As String Implements IData.GetData Return "DataDB" End Function End Class Friend Class DataWeb : Implements IData Friend Function GetData() As String Implements IData.GetData Return "DataWeb" End Function End Class 

Questa è una domanda molto vecchia e la versione java-8 ha aggiunto più funzionalità e potenza all’interfaccia.

Una dichiarazione dell’interfaccia può contenere

  1. firme del metodo
  2. metodi predefiniti
  3. metodi statici
  4. definizioni costanti.

Gli unici metodi con implementazioni nell’interfaccia sono metodi predefiniti e statici .

Usi di interfaccia:

  1. Per definire un contratto
  2. Colbind le classi non correlate con ha delle capacità (ad es. Le classi che implementano un’interfaccia Serializable possono o non possono avere alcuna relazione tra loro eccetto implementare quell’interfaccia
  3. Per fornire un’implementazione intercambiabile, ad esempio Strategy_pattern
  4. 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
  5. Organizza i metodi di supporto nelle tue librerie con metodi statici (puoi mantenere i metodi statici specifici per un’interfaccia nella stessa interfaccia piuttosto che in una class separata)

Dai un’occhiata a questa domanda SE relativa per un esempio di codice per capire meglio i concetti:

Come dovrei aver spiegato la differenza tra un’interfaccia e una class astratta?

Tornando alle tue domande:

Q1. Poiché le interfacce hanno solo metodi astratti (nessun codice), quindi come possiamo dire che se implementiamo un’interfaccia, allora è ereditaria? Non stiamo usando il suo codice.

Q2. Se l’implementazione di un’interfaccia non è un’eredità, come vengono utilizzate le interfacce per ottenere l’ereditarietà multipla?

L’interfaccia può contenere codice per metodi statici e predefiniti . Questi metodi predefiniti offrono compatibilità con le versioni precedenti e i metodi statici forniscono funzioni di supporto / utilità .

Non si può avere vera ereditarietà multipla in java e l’interfaccia non è il modo per ottenerla. L’interfaccia può contenere solo costanti. Quindi non puoi ereditare lo stato ma puoi implementare il comportamento.

È ansible sostituire l’ ereditarietà con la capacità . L’interfaccia offre molteplici funzionalità per l’implementazione delle classi.

Q3. In ogni caso, qual è il vantaggio di utilizzare le interfacce? Non stanno avendo alcun codice. Abbiamo bisogno di scrivere codice ancora e ancora in tutte le classi che implementiamo.

Fare riferimento alla sezione ” usi di interfaccia ” nella mia risposta.

Q1. Poiché le interfacce hanno solo metodi astratti (nessun codice), quindi come possiamo dire che se implementiamo un’interfaccia, allora è ereditaria? Non stiamo usando il suo codice.

Sfortunatamente, nell’uso colloquiale, l’ inheritance parole è ancora frequentemente usata quando una class implementa un’interfaccia, sebbene l’ interface implementation sia un termine preferibile – IMO, il termine inheritance deve essere usato rigorosamente con l’ereditarietà di una class concreta o astratta. In linguaggi come C ++ e C #, la stessa syntax (ovvero Subclass : Superclass e Class : Interface ) viene utilizzata sia per l’ereditarietà delle classi che per l’implementazione dell’interfaccia, che può aver contribuito alla diffusione dell’abuso inheritance parole con le interfacce. Java ha una syntax diversa per extend una class piuttosto che implement un’interfaccia, che è una buona cosa.

Q2 Se l’implementazione di un’interfaccia non è un’eredità, allora come le interfacce vengono utilizzate per ottenere l’ereditarietà multipla?

È ansible ottenere l ‘”effetto” dell’ereditarietà multipla attraverso la composizione, implementando più interfacce su una class e fornendo quindi implementazioni per tutti i metodi, le proprietà e gli eventi richiesti da tutte le interfacce sulla class. Una tecnica comune per fare ciò con le classi concrete consiste nel fare relazioni di “has-a” (composizione) con le classi che implementano le interfacce esterne “legando” l’implementazione a ciascuna implementazione di class interna. (Linguaggi come C ++ supportano direttamente l’ereditarietà concreta, ma creano altri potenziali problemi come il problema dei diamanti).

Q3 In ogni caso, qual è il vantaggio di usare le interfacce? Non stanno avendo alcun codice. Abbiamo bisogno di scrivere codice ancora e ancora in tutte le classi che implementiamo.

Le interfacce consentono alle classi esistenti (ad es. Framework) di interagire con le nuove classi senza averle mai viste prima, a causa della capacità di comunicare attraverso un’interfaccia conosciuta. Pensa a un’interfaccia come a un contratto. Implementando questa interfaccia su una class, sei contrattualmente obbligato a soddisfare gli obblighi richiesti e, una volta implementato questo contratto, la tua class dovrebbe poter essere utilizzata in modo intercambiabile con qualsiasi altro codice che consumi l’interfaccia.

Esempio di mondo reale

Un esempio di “mondo reale” sarebbe la legislazione e la convenzione (interfaccia) che circonda una presa elettrica in un determinato paese. Ogni elettrodomestico inserito nella presa deve soddisfare le specifiche (contratto) stabilite dalle autorità per la presa, ad esempio il posizionamento della linea, i fili di terra e di neutro, la posizione e la colorazione dell’interruttore on / off e la conformità la tensione elettrica, la frequenza e la corrente massima che verranno fornite attraverso l’ interface quando è accesa.

Il vantaggio di disaccoppiare l’interfaccia (ovvero una presa a muro standard) piuttosto che i soli cavi di saldatura è che è ansible colbind (e scolbind) un ventilatore, un bollitore, un doppio adattatore o qualche nuovo apparecchio da inventare l’anno prossimo in esso , anche se questo apparecchio non esisteva quando l’interfaccia è stata progettata. Perché? Perché sarà conforms ai requisiti dell’interfaccia.

Perché usare le interfacce?

Le interfacce sono ideali per l’accoppiamento lento di classi e sono uno dei cardini del paradigma SOLID dello zio Bob, in particolare il Dependency Inversion Principle e i Interface Segregation Principles .

In poche parole, assicurando che le dipendenze tra le classi siano accoppiate solo su interfacce (astrazioni) e non su altre classi concrete, consente di sostituire la dipendenza con qualsiasi altra implementazione di class che soddisfi i requisiti dell’interfaccia.

Nel test, stub e mock di dipendenze possono essere usati per testare ogni class, e l’interazione che la class ha con la dipendenza può essere “spiata” su.

L’ereditarietà è quando una class deriva da un’altra class (che può essere astratta) o un’interfaccia. Il punto più forte di object oriented (ereditarietà) non è il riutilizzo del codice (ci sono molti modi per farlo), ma il polimorfismo.

Il polimorfismo è quando si dispone di codice che utilizza l’interfaccia, che può essere di qualsiasi class derivata da tale interfaccia. Per esempio, posso avere un tale metodo: public void Pet (IAnimal animal) e questo metodo otterrà un object che è un’istanza di Dog o Cat che eredita da IAnimal. o posso avere un tale codice: IAnimal animal e quindi posso chiamare un metodo di questa interfaccia: animal.Eat () che Dog o Cat può implementare in un modo diverso.

Il vantaggio principale delle interfacce è che puoi ereditare da alcune di esse, ma se devi ereditare da una sola puoi anche usare una class astratta. Ecco un articolo che spiega di più sulle differenze tra una class astratta e un’interfaccia: http://www.codeproject.com/KB/cs/abstractsvsinterfaces.aspx

Entrambi i metodi funzionano (interfacce e ereditarietà multipla).

Breve risposta pratica breve

Le interfacce sono migliori quando si hanno diversi anni di esperienza nell’uso dell’ereditarietà multipla con Super Class con la sola definizione del metodo e nessun codice.

Una domanda complementare potrebbe essere: “Come e perché trasferire il codice dalle classi astratte alle interfacce”.

Se non hai molte classi astratte, puoi saltare le interfacce.

Non correre a usare le interfacce.

Risposta lunga e noiosa

Le interfacce sono molto simili o addirittura equivalenti alle Classi astratte.

Se il tuo codice ha molte classi astratte, allora è tempo che inizi a pensare in termini di interfacce.

Il seguente codice con classi astratte:


MyStreamsClasses.java

 /* File name : MyStreamsClasses.java */ import java.lang.*; // Any number of import statements public abstract class InputStream { public void ReadObject(Object MyObject); } public abstract class OutputStream { public void WriteObject(Object MyObject); } public abstract class InputOutputStream imnplements InputStream, OutputStream { public void DoSomethingElse(); } 

Può essere sostituito con:


MyStreamsInterfaces.java

 /* File name : MyStreamsInterfaces.java */ import java.lang.*; // Any number of import statements public interface InputStream { public void ReadObject(Object MyObject); } public interface OutputStream { public void WriteObject(Object MyObject); } public interface InputOutputStream extends InputStream, OutputStream { public void DoSomethingElse(); } 

Saluti.

Q1. Poiché le interfacce hanno solo metodi astratti (nessun codice), quindi come possiamo dire che se stiamo implementando un’interfaccia, allora è ereditaria? Non stiamo usando il suo codice.

Non è un’eredità uguale. È solo simile. Lasciatemi spiegare:

 VolvoV3 extends VolvoV2, and VolvoV2 extends Volvo (Class) VolvoV3 extends VolvoV2, and VolvoV2 implements Volvo (Interface) line1: Volvo v = new VolvoV2(); line2: Volvo v = new VolvoV3(); 

Se vedi solo line1 e line2 puoi dedurre che VolvoV2 e VolvoV3 hanno lo stesso tipo. Non puoi dedurre se Volvo una superclass o Volvo è un’interfaccia.

Q2. Se l’implementazione di un’interfaccia non è un’eredità, come vengono utilizzate le interfacce per ottenere più eredità?

Ora usando le interfacce:

 VolvoXC90 implements XCModel and Volvo (Interface) VolvoXC95 implements XCModel and Volvo (Interface) line1: Volvo a = new VolvoXC90(); line2: Volvo a = new VolvoXC95(); line3: XCModel a = new VolvoXC95(); 

Se vedi solo line1 e line2 puoi dedurre che VolvoXC90 e VolvoXC95 hanno lo stesso tipo (Volvo). Non puoi dedurre che Volvo sia una superclass o che Volvo sia un’interfaccia.

Se vedi solo line2 e line3 puoi dedurre che Volvo95 implementa due tipi, XCModel e Volvo, in Java sai che almeno uno deve essere un’interfaccia. Se questo codice è stato scritto in C ++, ad esempio, potrebbero essere entrambe le classi. Pertanto, più eredità.

Q3. In ogni caso, qual è il vantaggio di utilizzare le interfacce? Non stanno avendo alcun codice. Abbiamo bisogno di scrivere codice ancora e ancora in tutte le classi che implementiamo.

Immagina un sistema in cui usi una class VolvoXC90 in 200 altre classi.

 VolvoXC90 v = new VolvoXC90(); 

Se devi far evolvere il tuo sistema per avviare VolvoXC95 devi modificare altre 200 classi.

Ora, immagina un sistema in cui utilizzi un’interfaccia Volvo in 10.000.000 di classi.

 // Create VolvoXC90 but now we need to create VolvoXC95 Volvo v = new VolvoFactory().newCurrentVolvoModel(); 

Ora, se hai bisogno di evolvere il tuo sistema per creare i modelli VolvoXC95 devi modificare solo una class, la Factory.

È una domanda di buon senso. Se il tuo sistema è composto solo da poche classi e pochi aggiornamenti utilizzano le interfacce ovunque è controproducente. Per i grandi sistemi, può farti risparmiare molto dolore ed evitare il rischio di adottare interfacce.

Vi consiglio di leggere di più sui principi SOLID e leggere il libro Effective Java. Ha buone lezioni da ingegneri esperti di software.

Le interfacce sono fatte in modo tale che una class implementerà la funzionalità all’interno dell’interfaccia e si comporterà secondo questa interfaccia.

Vecchia domanda Sono sorpreso che nessuno abbia citato le fonti canoniche: Java: una panoramica di James Gosling, Design Patterns: elementi del software riutilizzabile orientato agli oggetti della Gang of Four o Java efficace di Joshua Bloch (tra le altre fonti).

Inizierò con una citazione:

Un’interfaccia è semplicemente una specifica di un insieme di metodi a cui un object risponde. Non include alcuna variabile di istanza o implementazione. Le interfacce possono essere ereditate in modo multiplo (a differenza delle classi) e possono essere utilizzate in un modo più flessibile rispetto alla solita struttura di ereditarietà di classi rigide. (Gosling, p.8)

Ora, prendiamo le vostre ipotesi e domande una per una (ignorerò volontariamente le funzionalità di Java 8).

ipotesi

L’interfaccia è raccolta di SOLO metodi astratti e campi finali.

Hai visto la parola chiave abstract nelle interfacce Java? No. Quindi non dovresti considerare un’interfaccia come una raccolta di metodi astratti. Forse sei ingannato dalle cosiddette interfacce C ++, che sono classi con solo metodi virtuali puri. C ++, in base alla progettazione, non ha (e non ha bisogno di avere) interfacce, perché ha un’eredità più ampia.

Come spiegato da Gosling, dovresti considerare un’interfaccia come “un insieme di metodi a cui un object risponde”. Mi piace vedere un’interfaccia e la documentazione associata come un contratto di servizio. Descrive cosa puoi aspettarti da un object che implementa quell’interfaccia. La documentazione deve specificare le pre e post-condizioni (ad esempio i parametri non devono essere nulli, l’output è sempre positivo, …) e gli invarianti (un metodo che non modifica lo stato interno dell’object). Questo contratto è il cuore, penso, di OOP.

Non c’è eredità multipla in Java.

Infatti.

JAVA omette molte caratteristiche del C ++ poco usate, scarsamente comprese e confuse che nella nostra esperienza portano più dolore che beneficio. Questo consiste principalmente nel sovraccarico dell’operatore (sebbene abbia sovraccarico di metodo), ereditarietà multipla e estese coercizioni automatiche. (Gosling, p.2)

Niente da aggiungere.

Le interfacce possono essere utilizzate per ottenere l’ereditarietà multipla in Java.

No, semplicemente perché non c’è eredità multipla in Java. Vedi sopra.

Un punto di forza dell’ereditarietà è che possiamo usare il codice della class base nella class derivata senza scriverlo di nuovo. Potrebbe essere questa la cosa più importante per l’eredità di essere lì.

Si chiama “ereditarietà dell’implementazione”. Come hai scritto, è un modo conveniente per riutilizzare il codice.

Ma ha una controparte importante:

le classi genitore spesso definiscono almeno parte della rappresentazione fisica delle sottoclassi. Poiché l’ereditarietà espone una sottoclass ai dettagli dell’implementazione del suo genitore, si dice spesso che “l’ereditarietà rompe l’incapsulamento” [Sny86]. L’implementazione di una sottoclass diviene così legata all’implementazione della sua class genitore che qualsiasi cambiamento nell’implementazione del genitore costringerà la sottoclass a cambiare. (GOF, 1.6)

(C’è una citazione simile in Bloch, punto 16.)

In realtà, l’eredità serve anche a un altro scopo:

L’ereditarietà delle classi combina l’ereditarietà delle interfacce e l’ereditarietà dell’implementazione. L’ereditarietà dell’interfaccia definisce una nuova interfaccia in termini di una o più interfacce esistenti. L’ereditarietà dell’implementazione definisce una nuova implementazione in termini di una o più implementazioni esistenti. (GOF, Appendice A)

Entrambi usano la parola chiave extends in Java. Potresti avere gerarchie di classi e gerarchie di interfacce. I primi condividono l’implementazione, i secondi condividono l’obbligo.

Domande

Q1. Poiché le interfacce hanno solo metodi astratti (nessun codice), quindi come possiamo dire che se implementiamo un’interfaccia, allora è ereditaria? Non stiamo usando il suo codice. **

L’implementazione di un’interfaccia non è ereditaria. È l’implementazione. Quindi la parola chiave implements .

Q2. Se l’implementazione di un’interfaccia non è un’eredità, in che modo le interfacce vengono utilizzate per ottenere un’ereditarietà multipla? **

Nessuna eredità multipla in Java. Vedi sopra.

Q3. In ogni caso, qual è il vantaggio di utilizzare le interfacce? Non stanno avendo alcun codice. Abbiamo bisogno di scrivere codice più e più volte in tutte le classi che implementiamo. / Allora perché creare interfacce? / Quali sono i vantaggi esatti dell’uso delle interfacce? E ‘davvero l’ereditarietà multipla che otteniamo usando le interfacce?

La domanda più importante è: perché ti piacerebbe avere ereditarietà multipla? Posso pensare a due risposte: 1. per dare tipi più piccoli a un object; 2. per riutilizzare il codice.

Fornisci tipi più piccoli a un object

In OOP, un object può avere tipi diversi . Ad esempio in Java, un ArrayList ha i seguenti tipi: Serializable , Iterable , Iterable , Collection , List , RandomAccess , AbstractList , AbstractCollection e Object (I hope Non ho dimenticato nessuno) Se un object ha tipi diversi, i vari consumatori potranno usarlo senza essere consapevole delle sue specificità. Ho bisogno di un Iterable e tu mi dai un ArrayList ? Va bene. Ma se ora ho bisogno di una List e tu mi dai una ArrayList , va bene lo stesso. Eccetera.

Come si digita un object in OOP? Hai preso l’interfaccia Runnable come esempio e questo esempio è perfetto per illustrare la risposta a questa domanda. Cito il documento ufficiale di Java:

Inoltre, Runnable fornisce i mezzi affinché una class sia triggers senza sottoclass Thread.

Ecco il punto: l’ ereditarietà è un modo conveniente di digitare oggetti. Vuoi creare una discussione? Prendiamo sottoclass la class Thread . Vuoi che un object abbia tipi diversi, usiamo l’ereditarietà mutliple. Argh. Non esiste in Java. (In C ++, se vuoi che un object abbia tipi diversi, l’ereditarietà multipla è la strada da percorrere).

Come dare più tipi a un object allora? In Java, puoi digitare direttamente il tuo object. Questo è quello che fai quando la tua class implements l’interfaccia Runnable . Perché usare Runnable se sei un fan dell’ereditarietà? Forse perché la tua class è già una sottoclass di un’altra class, diciamo A Ora la tua class ha due tipi: A e Runnable .

Con più interfacce, puoi dare più tipi a un object. Devi solo creare una class che implements più interfacce. Finché sei conforms ai contratti, va bene.

Riutilizzare il codice

Questo è un argomento difficile; Ho già citato il GOF per rompere l’incapsulamento. Un’altra risposta ha menzionato il problema dei diamanti. Potresti anche pensare al Principio della singola responsabilità:

Una class dovrebbe avere solo una ragione per cambiare. (Robert C. Martin, Agile Software Development, Principles, Patterns and Practices)

Avere una class genitore può dare a una class un motivo per cambiare, oltre alle proprie responsabilità:

L’implementazione della superclass può cambiare da versione a versione e, in tal caso, la sottoclass potrebbe interrompersi, anche se il suo codice non è stato toccato. Di conseguenza, una sottoclass deve evolvere in tandem con la sua superclass (Bloch, item 16).

Vorrei aggiungere un problema più prosaico: ho sempre una sensazione strana quando cerco di trovare il codice sorgente di un metodo in una class e non riesco a trovarlo. Quindi ricordo: deve essere definito da qualche parte nella class genitore. O nella class dei nonni. O forse anche più in alto. Un buon IDE è una risorsa preziosa in questo caso, ma rimane, nella mia mente, qualcosa di magico. Niente di simile con le gerarchie di interfacce, dal momento che javadoc è l’unica cosa di cui ho bisogno: una scorciatoia da tastiera nell’IDE e ho capito.

Il diritto alla successione presenta vantaggi:

È sicuro utilizzare l’ereditarietà all’interno di un pacchetto, in cui la sottoclass e le implementazioni della superclass sono sotto il controllo degli stessi programmatori. È anche sicuro utilizzare l’ereditarietà quando si estendono classi progettate e documentate specificamente per l’estensione (articolo 17: progettare e documentare per ereditarietà o altrimenti vietarlo). (Bloch, item 16)

Un esempio di una class “specificamente progettato e documentato per l’estensione” in Java è AbstractList .

Ma Bloch e GOF insistono su questo: “Favorisci la composizione sull’eredità”:

La delega è un modo per rendere la composizione così potente da poter essere riutilizzata come eredità [Lie86, JZ91]. In delega, due oggetti sono coinvolti nella gestione di una richiesta: un object ricevente delega le operazioni al suo delegato. Questo è analogo alle sottoclassi che rimandano le richieste alle classi parent. (GOF p.32)

Se si utilizza la composizione, non sarà necessario scrivere lo stesso codice più e più volte. Basta creare una class che gestisca le duplicazioni e si passa un’istanza di questa class alle classi che implementano l’interfaccia. È un modo molto semplice per riutilizzare il codice. E questo ti aiuta a seguire il Principio di Responsabilità Unica e rendere il codice più testabile. Rust and Go non hanno ereditarietà (non hanno nemmeno classi), ma non penso che il codice sia più ridondante rispetto ad altri linguaggi OOP.

Inoltre, se usi la composizione, ti ritroverai naturalmente ad utilizzare le interfacce per dare al tuo codice la struttura e la flessibilità di cui ha bisogno (vedi altre risposte sui casi d’uso delle interfacce).

Nota: è ansible condividere il codice con le interfacce Java 8

E infine, un’ultima citazione:

Durante la memorabile sessione di domande e risposte, qualcuno gli ha chiesto [James Gosling]: “Se potessi fare di nuovo Java, cosa cambieresti?” “Vorrei lasciare le lezioni” (ovunque sulla rete, non so se questo è vero)

interfacce

Un’interfaccia è un contratto che definisce come interagire con un object. Sono utili per esprimere il modo in cui i tuoi interni intendono interagire con un object. Dopo Dependency Inversion la tua API pubblica avrebbe tutti i parametri espressi con le interfacce. Non ti importa come fa ciò che ti serve, solo che fa esattamente quello che ti serve.

Esempio: potrebbe essere semplicemente necessario un Vehicle per trasportare merci, non ti interessa il particolare modo di trasporto.

Eredità

L’ereditarietà è un’estensione di una particolare implementazione. Tale implementazione può o non può soddisfare una particolare interfaccia. Dovresti aspettarti un antenato di una particolare implementazione solo se ti interessa come.

Esempio: potrebbe essere necessario un Plane implementazione di un veicolo per il trasporto veloce.

Composizione

La composizione può essere utilizzata come alternativa all’eredità. Invece della class che estende una class base, viene creata con oggetti che implementano porzioni più piccole della responsabilità della class principale. La composizione è utilizzata nel facade pattern della facade pattern e nel decorator pattern .

Esempio: è ansible creare una DuckBoat ( DUKW ) che implementa LandVehicle e WaterVehicle che implementano entrambi Vehicle composto da implementazioni Truck e Boat .

risposte

Q1. Poiché le interfacce hanno solo metodi astratti (nessun codice), quindi come possiamo dire che se implementiamo un’interfaccia, allora è ereditaria? Non stiamo usando il suo codice.

Le interfacce non sono ereditarietà. L’implementazione di un’interfaccia indica che si intende che la class operi nel modo definito dall’interfaccia. L’ereditarietà avviene quando si ha un antenato comune e si riceve lo stesso comportamento ( inherit ) dell’antenato, quindi non è necessario definirlo.

Q2. Se l’implementazione di un’interfaccia non è un’eredità, come vengono utilizzate le interfacce per ottenere l’ereditarietà multipla?

Le interfacce non raggiungono eredità multiple. Esprimono che una class può essere adatta a più ruoli.

Q3. In ogni caso, qual è il vantaggio di utilizzare le interfacce? Non stanno avendo alcun codice. Abbiamo bisogno di scrivere codice ancora e ancora in tutte le classi che implementiamo.

Uno dei principali vantaggi delle interfacce è quello di fornire una separazione dei problemi:

  • Puoi scrivere una class che fa qualcosa con un’altra class senza preoccuparsi di come viene implementata quella class.
  • Qualsiasi sviluppo futuro può essere compatibile con la tua implementazione senza la necessità di estendere una particolare class base.

Nello spirito di DRY è ansible scrivere un’implementazione che soddisfi un’interfaccia e modificarla rispettando il open/closed principal se si fa leva sulla composizione.