Classe interfaccia vs base


767

Quando dovrei usare un'interfaccia e quando dovrei usare una classe base?

Dovrebbe essere sempre un'interfaccia se non desidero effettivamente definire un'implementazione di base dei metodi?

Se ho una classe Dog and Cat. Perché dovrei voler implementare IPet invece di PetBase? Posso capire di avere interfacce per ISheds o IBark (IMakesNoise?), Perché possono essere posizionati su un animale domestico per animale domestico, ma non capisco quale usare per un animale domestico generico.


11
Solo un punto che penso dovresti prendere in considerazione: le interfacce possono porre diversi limiti di cui potresti non essere a conoscenza fino a fasi molto tarde. Ad esempio, con. un dolore).
synhershko,

1
Questa domanda potrebbe aiutare a comprendere il concetto di interfacce. stackoverflow.com/q/8531292/1055241
gprathour

Sono solo curioso. Ho incontrato nel CLR via C # il brano seguente: I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation.. Non riesco a capire cosa si intende nell'estratto. Possiamo creare alcuni tipi di base e creare un tipo derivato per ognuno di essi, in modo che uno sviluppatore possa scegliere un tipo di base. Qualcuno potrebbe spiegare, per favore, cosa mi sto perdendo? Credo che possa far parte di questa domanda. O dovrei postarne un altro sull'estratto specifico?
qqqqqqq

Risposte:


501

Prendiamo il tuo esempio di una classe Dog and a Cat e illustriamo usando C #:

Sia un cane che un gatto sono animali, in particolare mammiferi quadruplicati (gli animali sono forse troppo generici). Supponiamo che tu abbia un Mammal di classe astratta, per entrambi:

public abstract class Mammal

Questa classe base avrà probabilmente metodi predefiniti come:

  • Alimentazione
  • Compagno

Tutti questi sono comportamenti che hanno più o meno la stessa implementazione tra le due specie. Per definirlo avrai:

public class Dog : Mammal
public class Cat : Mammal

Supponiamo ora che ci siano altri mammiferi, che di solito vedremo in uno zoo:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

Questo sarà ancora valido perché al centro della funzionalità Feed()e Mate()sarà sempre lo stesso.

Tuttavia, giraffe, rinoceronti e ippopotami non sono esattamente animali da cui è possibile realizzare animali domestici. Ecco dove un'interfaccia sarà utile:

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

L'attuazione per il contratto di cui sopra non sarà la stessa tra un cane e un gatto; mettere le loro implementazioni in una classe astratta da ereditare sarà una cattiva idea.

Le definizioni di cani e gatti dovrebbero ora apparire come:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

Teoricamente puoi sovrascriverli da una classe base superiore, ma essenzialmente un'interfaccia ti consente di aggiungere solo le cose di cui hai bisogno in una classe senza bisogno di ereditarietà.

Di conseguenza, poiché di solito è possibile ereditare solo da una classe astratta (nella maggior parte dei linguaggi OO tipicamente statici che è ... le eccezioni includono C ++) ma essere in grado di implementare più interfacce, consente di costruire oggetti in modo strettamente necessario .


149
Non penso sia così semplice. Hai leggermente modificato la domanda (requisiti) in modo che l'interfaccia abbia più senso. Dovresti sempre chiederti se stai definendo un contratto (interfaccia) o un'implementazione condivisa (classe base).
David Pokluda,

18
L'interfaccia è un contratto. Stai solo esponendo la parte del contratto richiesta da un servizio. Se hai un 'PettingZoo', sicuramente non vuoi esporre 'Mate' -ing all'utente!
Anthony Mastrean,

10
@David Touche, anche se l'ho fatto per illustrare meglio a cosa serve un'interfaccia e a cosa serve una classe astratta di fronte alla sua comprensione. Un cane e un gatto non sembrano essere un requisito rigido!
Jon Limjap,

2
Va notato che in ambienti JIT interpretati (in particolare JVM), le chiamate ai metodi virtuali sono significativamente più veloci delle chiamate ai metodi di interfaccia , a seconda del contesto. Sottolineo il "contesto" perché la JVM può spesso ottimizzare le ricerche del metodo lento. (ad esempio, se un elenco di ereditari di interfaccia tende ad essere istanze della stessa classe. Questo purtroppo rende il benchmarking un po 'difficile.) Se stai cercando di ottimizzare qualcosa di sensibile alle prestazioni, e solo allora, dovresti considerare questo.
Philip Guin,

14
Inoltre, un'interfaccia consente una roccia da compagnia, permettendole di essere bagnata e di ricevere insegnamenti, ma l'alimentazione e l'accoppiamento non sarebbero supportati perché sarebbe ridicolo per una roccia.
eclux,

146

Bene, Josh Bloch si è detto in Effective Java 2d :

Preferisci le interfacce rispetto alle classi astratte

Alcuni punti principali:

  • Le classi esistenti possono essere facilmente adattate per implementare una nuova interfaccia . Tutto quello che devi fare è aggiungere i metodi richiesti se non esistono ancora e aggiungere una clausola implementa alla dichiarazione di classe.

  • Le interfacce sono ideali per la definizione di mixin . In parole povere, un mixin è un tipo che una classe può implementare oltre al suo "tipo primario" per dichiarare che fornisce un comportamento facoltativo. Ad esempio, Comparable è un'interfaccia mixin che consente a una classe di dichiarare che le sue istanze sono ordinate rispetto ad altri oggetti reciprocamente comparabili.

  • Le interfacce consentono la costruzione di framework di tipo non gerarchico . Le gerarchie di tipi sono ottime per organizzare alcune cose, ma altre cose non rientrano ordinatamente in una rigida gerarchia.

  • Le interfacce consentono potenziamenti di funzionalità potenti e sicuri tramite il linguaggio della classe avvolgente. Se usi le classi astratte per definire i tipi, lasci il programmatore che vuole aggiungere funzionalità senza alternative se non usare l'ereditarietà.

Inoltre, puoi combinare le virtù di interfacce e classi astratte fornendo una classe di implementazione scheletrica astratta da abbinare a ciascuna interfaccia non banale che esporti.

D'altra parte, le interfacce sono molto difficili da evolvere. Se aggiungi un metodo a un'interfaccia, si romperanno tutte le sue implementazioni.

PS .: Compra il libro. È molto più dettagliato.


71
Ogni volta che è necessario apportare una modifica a un'interfaccia, il modo ininterrotto per farlo è quello di creare una nuova interfaccia che eredita da quella precedente. Ciò preserva le implementazioni esistenti e ti consente di fare tutto ciò che desideri in quello nuovo.
Scott Lawrence,

5
abbiamo "Principio di segregazione dell'interfaccia". Questo principio ci insegna a prenderci cura di come scriviamo le nostre interfacce. Quando scriviamo le nostre interfacce, dovremmo occuparci di aggiungere solo i metodi che dovrebbero essere presenti. Se aggiungiamo metodi che non dovrebbero essere presenti, anche le classi che implementano l'interfaccia dovranno implementare tali metodi. Ad esempio, se creiamo un'interfaccia chiamata Worker e aggiungiamo una pausa pranzo del metodo, tutti i lavoratori dovranno implementarla. Che cosa succede se il lavoratore è un robot? In conclusione, le interfacce contenenti metodi che non sono specifici ad esso sono chiamate interfacce inquinate o grasse.
Eklavyaa,

A partire da Java 8, i metodi predefiniti consentono di aggiungere nuove funzionalità alle interfacce e garantire la retrocompatibilità per le classi esistenti che implementano tale interfaccia. I metodi predefiniti vengono richiamati per impostazione predefinita, se non vengono sovrascritti nella classe di implementazione. Tutta la classe di implementazione può ignorare i metodi predefiniti o può chiamarli direttamente usando instance.defaultMethod ()
Arpit Rastogi il

118

Le interfacce e le classi di base rappresentano due diverse forme di relazioni.

L'ereditarietà (classi di base) rappresenta una relazione "is-a". Ad esempio un cane o un gatto "è-un" animale domestico. Questa relazione rappresenta sempre lo scopo (unico) della classe (in combinato disposto con il "principio della responsabilità singola" ).

Le interfacce , d'altra parte, rappresentano funzionalità aggiuntive di una classe. Lo definirei una relazione "is", come in " Foois usa e getta", quindi l' IDisposableinterfaccia in C #.


10
Tra tutte le risposte, questa offre la migliore combinazione di brevità senza perdita di chiarezza
Matt Wilko,

Qualcuno una volta mi ha detto di usare un'interfaccia quando c'è una relazione "has-a". Non sono sicuro che sia sempre vero; Il mio laptop ha uno schermo, quindi il laptop dovrebbe implementare IScreen o avere una proprietà Screen? Quest'ultimo mi sembra più naturale.
Berend,

1
@berend divertente che tu lo scriva perché gli schermi sono implementati tramite interfacce - VGA, HDMI ecc.
Konstantin

Solo perché è OOP, dobbiamo estendere la nostra immaginazione per seguire scenari di vita reale. Non si applica sempre.
Crismogramma

110

Lo stile moderno è definire IPet e PetBase.

Il vantaggio dell'interfaccia è che altri codici possono usarlo senza alcun legame con altri codici eseguibili. Completamente "pulito". Anche le interfacce possono essere mescolate.

Ma le classi di base sono utili per implementazioni semplici e utilità comuni. Quindi fornire anche una classe base astratta per risparmiare tempo e codice.


prendi la tua torta e mangiala anche tu!
Darren Kopp,

3
Un'interfaccia definisce come altre classi possono usare il tuo codice. Una classe base aiuta gli implementatori a implementare la tua interfaccia. Due cose diverse per due scopi diversi.
Bill Michell,

27
Non c'è niente di "moderno" qui. La classe di base e l'interfaccia con la stessa API sono semplicemente ridondanti. Puoi usare questo approccio in alcuni casi ma non dovresti generalizzare!
Smentek,

3
Il commento più supportato alla seconda risposta supportata in realtà non è d'accordo con la risposta stessa, interessante. Devo dire che vedo che coesistono molti esempi di interfaccia e classe base. In questo senso è il modo "moderno". Ad esempio, nel modello MVVM, esiste effettivamente la classe ViewModelBase che implementa INotifyPropertyChanged. Ma quando il mio collega mi ha chiesto perché avevo la classe base, invece di implementare l'interfaccia in ogni modello di vista, non so come convincerlo
tete,

1
Corretta. Non è una questione di 1 o dell'altro. Esistono per risolvere 2 problemi molto diversi. Le interfacce sono contratti che una classe attuatrice deve affrontare. Hanno trovato il favore recente (a volte fanaticamente) per gli aspetti IoC e TDD. Le classi Abstract / Base servono per raggruppare logiche e proprietà gerarchicamente comuni. Riduce il codice duplicato che a sua volta aumenta la manutenibilità delle soluzioni e lo rende meno soggetto a errori.
Vieni il

63

interfacce

  • Definisce il contratto tra 2 moduli. Non è possibile implementare.
  • La maggior parte delle lingue consente di implementare più interfacce
  • La modifica di un'interfaccia è un cambiamento di rottura. Tutte le implementazioni devono essere ricompilate / modificate.
  • Tutti i membri sono pubblici. Le implementazioni devono implementare tutti i membri.
  • Le interfacce aiutano nel disaccoppiamento. È possibile utilizzare framework finti per deridere qualsiasi cosa dietro un'interfaccia
  • Le interfacce normalmente indicano un tipo di comportamento
  • Le implementazioni dell'interfaccia sono disaccoppiate / isolate l'una dall'altra

Classi di base

  • Consente di aggiungere alcune implementazioni predefinite ottenute gratuitamente per derivazione
  • Tranne C ++, puoi derivare solo da una classe. Anche se potrebbe provenire da più classi, di solito è una cattiva idea.
  • Cambiare la classe base è relativamente facile. Le derivazioni non devono fare nulla di speciale
  • Le classi di base possono dichiarare funzioni protette e pubbliche a cui è possibile accedere tramite derivazioni
  • Le classi Base astratte non possono essere derise facilmente come le interfacce
  • Le classi base normalmente indicano la gerarchia dei tipi (IS A)
  • Le derivazioni di classe possono dipendere da alcuni comportamenti di base (avere una conoscenza complessa dell'implementazione del genitore). Le cose possono essere disordinate se si modifica l'implementazione di base per un ragazzo e si interrompono gli altri.

Nota: le linee guida per la progettazione del framework consigliano di utilizzare le classi di base (al contrario delle interfacce) perché sono versioni migliori. L'aggiunta di un nuovo metodo a una classe di base astratta in vNext è una modifica continua.
Gishu,

59

In generale, dovresti favorire le interfacce rispetto alle classi astratte. Un motivo per utilizzare una classe astratta è se si dispone di un'implementazione comune tra classi concrete. Ovviamente, dovresti comunque dichiarare un'interfaccia (IPet) e avere una classe astratta (PetBase) implementare quell'interfaccia. Usando interfacce piccole e distinte, puoi usare i multipli per migliorare ulteriormente la flessibilità. Le interfacce consentono la massima flessibilità e portabilità dei tipi oltre i confini. Quando si passano i riferimenti oltre i confini, passare sempre l'interfaccia e non il tipo concreto. Ciò consente alla parte ricevente di determinare un'implementazione concreta e offre la massima flessibilità. Questo è assolutamente vero quando si programma in modo TDD / BDD.

The Gang of Four ha affermato nel loro libro "Poiché l'eredità espone una sottoclasse ai dettagli dell'implementazione dei suoi genitori, si dice spesso che" l'ereditarietà rompe l'incapsulamento ". Credo che questo sia vero.


Yeesh. Personalmente, credo che questo sia un culo all'indietro. Le interfacce dovrebbero contenere la minima funzionalità minima per un tipo e le classi di base dovrebbero fornire un ricco framework su cui è possibile costruire la personalizzazione. Inseriscilo in un'interfaccia e hai appena reso molto difficile l'implementazione.

Nessuno ha mai detto che le tue interfacce debbano essere enormi. Interfacce più piccole e multiple e classi base più ricche rendono un'API eccezionale.
Kilhoffer,

3
Sono solo io o la maggior parte delle classi di "lavoratori comuni" condividono un'implementazione comune? In quel contesto va contro la tua regola generale di favorire le interfacce. Vorrei riaffermare la tua generalizzazione in 2 generalizzazioni: quelle classi comuni che contengono nessuna o poca logica dovrebbero implementare un'interfaccia. Le classi comuni che contengono una quantità "discreta" di logica dovrebbero derivare da una classe base (poiché molto probabilmente condivideranno la funzionalità).
goku_da_master,

@Kilhoffer "Le interfacce consentono la massima flessibilità e portabilità dei tipi oltre i confini", si prega di elaborare questa affermazione.
JAVA,

49

Questo è piuttosto specifico per .NET, ma il libro Linee guida per la progettazione del framework sostiene che in generale le classi offrono maggiore flessibilità in un framework in evoluzione. Una volta che un'interfaccia viene spedita, non hai la possibilità di cambiarla senza rompere il codice che ha usato quell'interfaccia. Con una classe tuttavia, è possibile modificarlo e non interrompere il codice che lo collega. Fintanto che apporti le giuste modifiche, che includono l'aggiunta di nuove funzionalità, sarai in grado di estendere ed evolvere il tuo codice.

Krzysztof Cwalina dice a pagina 81:

Nel corso delle tre versioni di .NET Framework, ho parlato di questa linea guida con alcuni sviluppatori del nostro team. Molti di loro, compresi quelli che inizialmente non erano d'accordo con le linee guida, hanno dichiarato di pentirsi di aver fornito alcune API come interfaccia. Non ho sentito nemmeno un caso in cui qualcuno si è pentito di aver spedito un corso.

Detto questo, c'è sicuramente spazio per le interfacce. Come linea guida generale fornire sempre un'implementazione di classe base astratta di un'interfaccia se non altro come esempio di un modo per implementare l'interfaccia. Nel migliore dei casi, quella classe di base farà risparmiare molto lavoro.


19

Juan,

Mi piace pensare alle interfacce come a un modo per caratterizzare una classe. Una particolare classe di razza di cane, ad esempio Yorkshire Terrier, può essere una discendente della classe di cane genitore, ma implementa anche IFurry, IStubby e IYippieDog. Quindi la classe definisce qual è la classe, ma l'interfaccia ci dice cose al riguardo.

Il vantaggio di questo è che mi permette, ad esempio, di raccogliere tutti gli IYippieDog e gettarli nella mia collezione Ocean. Quindi ora posso raggiungere un determinato insieme di oggetti e trovare quelli che soddisfano i criteri che sto esaminando senza ispezionare troppo attentamente la classe.

Trovo che le interfacce debbano davvero definire un sottoinsieme del comportamento pubblico di una classe. Se definisce tutto il comportamento pubblico per tutte le classi che implementano, di solito non deve esistere. Non mi dicono nulla di utile.

Questo pensiero però è contrario all'idea che ogni classe dovrebbe avere un'interfaccia e dovresti programmare l'interfaccia. Va bene, ma finisci con molte interfacce one to one verso le classi e questo crea confusione. Capisco che l'idea è che non costa davvero nulla da fare e ora puoi scambiare le cose con facilità. Tuttavia, trovo che lo faccio raramente. Il più delle volte sto solo modificando la classe esistente sul posto e ho gli stessi identici problemi che ho sempre fatto se l'interfaccia pubblica di quella classe deve essere cambiata, tranne che ora devo cambiarla in due punti.

Quindi, se pensi come me, diresti sicuramente che Cat and Dog sono IPectable. È una caratterizzazione che corrisponde a entrambi.

L'altro pezzo di questo è: dovrebbero avere la stessa classe base? La domanda è: devono essere generalmente trattati come la stessa cosa. Certamente sono entrambi Animali, ma si adatta a come li useremo insieme.

Di 'che voglio raccogliere tutte le classi di animali e metterle nel mio contenitore dell'Arca.

O devono essere mammiferi? Forse abbiamo bisogno di una specie di fabbrica di mungitura per animali?

Devono anche essere collegati tra loro? È sufficiente sapere solo che sono entrambi IPectable?

Sento spesso il desiderio di derivare un'intera gerarchia di classi quando ho davvero bisogno di una sola classe. Lo faccio in anticipo un giorno potrei averne bisogno e di solito non lo faccio mai. Anche quando lo faccio, di solito scopro che devo fare molto per risolverlo. Questo perché la prima classe che sto creando non è il cane, non sono così fortunato, è invece l'ornitorinco. Ora tutta la mia gerarchia di classi si basa sul caso bizzarro e ho un sacco di codice sprecato.

Ad un certo punto potresti anche scoprire che non tutti i gatti sono IPectable (come quello senza peli). Ora puoi spostare quell'interfaccia su tutte le classi derivate che si adattano. Scoprirai che un cambiamento molto meno grave che all'improvviso i gatti non derivano più da PectableBase.


18

Ecco la definizione di base e semplice dell'interfaccia e della classe base:

  • Classe base = ereditarietà degli oggetti.
  • Interfaccia = eredità funzionale.

Saluti


12

Raccomando di usare la composizione invece dell'ereditarietà quando possibile. Usa le interfacce ma usa gli oggetti membro per l'implementazione di base. In questo modo, puoi definire una fabbrica che costruisce i tuoi oggetti per comportarsi in un certo modo. Se si desidera modificare il comportamento, si crea un nuovo metodo factory (o factory astratto) che crea diversi tipi di oggetti secondari.

In alcuni casi, potresti scoprire che i tuoi oggetti primari non necessitano affatto di interfacce, se tutto il comportamento mutevole è definito negli oggetti helper.

Quindi, invece di IPet o PetBase, potresti finire con un animale domestico che ha un parametro IFurBehavior. Il parametro IFurBehavior è impostato dal metodo CreateDog () di PetFactory. È questo parametro che viene chiamato per il metodo shed ().

Se lo fai, scoprirai che il tuo codice è molto più flessibile e la maggior parte dei tuoi semplici oggetti si occupa di comportamenti di base a livello di sistema.

Consiglio questo modello anche in linguaggi a ereditarietà multipla.


12

È spiegato bene in questo articolo di Java World .

Personalmente, tendo a utilizzare le interfacce per definire le interfacce, ovvero parti del design del sistema che specificano come accedere a qualcosa.

Non è raro che avrò una classe che implementa una o più interfacce.

Classi astratte che utilizzo come base per qualcos'altro.

Quello che segue è un estratto dell'articolo di JavaWorld.com sopra citato , autore Tony Sintes, 20/04/01


Interfaccia vs. classe astratta

La scelta di interfacce e classi astratte non è una / o proposizione. Se hai bisogno di cambiare il tuo design, rendilo un'interfaccia. Tuttavia, potresti avere classi astratte che forniscono alcuni comportamenti predefiniti. Le classi astratte sono candidati eccellenti all'interno dei framework applicativi.

Le classi astratte ti consentono di definire alcuni comportamenti; costringono le tue sottoclassi a fornire ad altri. Ad esempio, se si dispone di un framework applicativo, una classe astratta può fornire servizi predefiniti come la gestione di eventi e messaggi. Tali servizi consentono all'applicazione di collegarsi al framework dell'applicazione. Tuttavia, esistono alcune funzionalità specifiche dell'applicazione che solo l'applicazione può eseguire. Tale funzionalità potrebbe includere attività di avvio e arresto, che spesso dipendono dall'applicazione. Quindi, invece di provare a definire quel comportamento stesso, la classe base astratta può dichiarare i metodi di arresto e avvio astratti. La classe base sa che ha bisogno di quei metodi, ma una classe astratta consente alla tua classe di ammettere che non sa come eseguire quelle azioni; sa solo che deve iniziare le azioni. Quando è il momento di avviare, la classe astratta può chiamare il metodo startup. Quando la classe base chiama questo metodo, Java chiama il metodo definito dalla classe figlio.

Molti sviluppatori dimenticano che una classe che definisce un metodo astratto può chiamare anche quel metodo. Le classi astratte sono un modo eccellente per creare gerarchie ereditarie pianificate. Sono anche una buona scelta per le classi non foglia nelle gerarchie di classi.

Classe vs. interfaccia

Alcuni dicono che dovresti definire tutte le classi in termini di interfacce, ma penso che la raccomandazione sembri un po 'estrema. Uso le interfacce quando vedo che qualcosa nel mio progetto cambierà frequentemente.

Ad esempio, il modello di strategia consente di scambiare nuovi algoritmi e processi nel programma senza alterare gli oggetti che li utilizzano. Un lettore multimediale potrebbe sapere come riprodurre CD, MP3 e file wav. Naturalmente, non si desidera codificare nel codice quegli algoritmi di riproduzione nel lettore; ciò renderà difficile l'aggiunta di un nuovo formato come AVI. Inoltre, il tuo codice sarà disseminato di dichiarazioni di casi inutili. E per aggiungere la beffa al danno, dovrai aggiornare quelle dichiarazioni di casi ogni volta che aggiungi un nuovo algoritmo. Tutto sommato, questo non è un modo di programmare orientato agli oggetti.

Con il modello di strategia, puoi semplicemente incapsulare l'algoritmo dietro un oggetto. In tal caso, è possibile fornire nuovi plug-in multimediali in qualsiasi momento. Chiamiamo la classe plug-in MediaStrategy. Quell'oggetto avrebbe un metodo: playStream (Stream s). Quindi per aggiungere un nuovo algoritmo, estendiamo semplicemente la nostra classe di algoritmi. Ora, quando il programma incontra il nuovo tipo di media, delega semplicemente la riproduzione dello stream alla nostra strategia multimediale. Ovviamente, avrai bisogno di un impianto idraulico per creare un'istanza corretta delle strategie dell'algoritmo di cui avrai bisogno.

Questo è un posto eccellente per usare un'interfaccia. Abbiamo usato il modello di strategia, che indica chiaramente un posto nel design che cambierà. Pertanto, è necessario definire la strategia come interfaccia. In genere dovresti favorire le interfacce rispetto all'ereditarietà quando vuoi che un oggetto abbia un certo tipo; in questo caso, MediaStrategy. Affidarsi all'eredità per l'identità del tipo è pericoloso; ti blocca in una particolare gerarchia ereditaria. Java non consente l'ereditarietà multipla, quindi non puoi estendere qualcosa che ti dia un'implementazione utile o più identità di tipo.


2
+1. "Affidarsi all'eredità per l'identità del tipo è pericoloso; ti blocca in una particolare gerarchia ereditaria." Quella frase descrive perfettamente le mie ragioni per preferire le interfacce.
Ingegnere

E oltre a ciò, anziché estenderlo, comporre l'implementazione dietro ogni metodo della tua interfaccia.
Ingegnere

10

Ricorda inoltre di non essere spazzato via in OO ( vedi blog ) e modella sempre gli oggetti in base al comportamento richiesto, se stavi progettando un'app in cui l'unico comportamento richiesto era un nome generico e una specie per un animale, allora avresti solo bisogno un animale di classe con una proprietà per il nome, anziché milioni di classi per ogni animale possibile nel mondo.


10

Ho una regola empirica approssimativa

Funzionalità: probabilmente diversa in tutte le parti: interfaccia.

Dati e funzionalità, le parti saranno per lo più le stesse, le parti diverse: classe astratta.

Dati e funzionalità, effettivamente funzionanti, se estesi solo con lievi modifiche: classe ordinaria (concreta)

Dati e funzionalità, nessuna modifica pianificata: classe ordinaria (concreta) con modificatore finale.

Dati e forse funzionalità: sola lettura: membri enum.

Questo è molto approssimativo e pronto e per nulla rigorosamente definito, ma esiste uno spettro da interfacce in cui tutto è destinato a essere convertito in enum in cui tutto è riparato un po 'come un file di sola lettura.


7

Le interfacce dovrebbero essere piccole. Veramente piccolo. Se stai davvero rompendo i tuoi oggetti, probabilmente le tue interfacce conterranno solo alcuni metodi e proprietà molto specifici.

Le classi astratte sono scorciatoie. Ci sono cose che condividono tutti i derivati ​​di PetBase che puoi codificare una volta ed essere fatto con? Se sì, allora è il momento di una lezione astratta.

Anche le classi astratte sono limitanti. Mentre ti danno un ottimo collegamento per la produzione di oggetti figlio, ogni dato oggetto può implementare solo una classe astratta. Molte volte trovo che questa sia una limitazione delle classi astratte, ed è per questo che utilizzo molte interfacce.

Le classi astratte possono contenere diverse interfacce. La tua classe astratta PetBase può implementare IPet (gli animali domestici hanno dei proprietari) e IDigestion (gli animali domestici mangiano, o almeno dovrebbero). Tuttavia, PetBase probabilmente non implementerà IMammal, poiché non tutti gli animali domestici sono mammiferi e non tutti i mammiferi sono animali domestici. È possibile aggiungere un MammalPetBase che estende PetBase e aggiungere IMammal. FishBase potrebbe avere PetBase e aggiungere IFish. IFish avrebbe ISwim e IUnderwaterBreather come interfacce.

Sì, il mio esempio è ampiamente complicato per il semplice esempio, ma fa parte della cosa grandiosa di come interfacce e classi astratte lavorano insieme.


7

Fonte : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/

C # è un linguaggio meraviglioso che è maturato ed evoluto negli ultimi 14 anni. Questo è ottimo per noi sviluppatori perché un linguaggio maturo ci fornisce una pletora di funzionalità linguistiche che sono a nostra disposizione.

Tuttavia, con molto potere diventa molta responsabilità. Alcune di queste funzionalità possono essere utilizzate in modo improprio o, a volte, è difficile capire perché si scelga di utilizzare una funzionalità rispetto a un'altra. Nel corso degli anni, una caratteristica con cui ho visto molti sviluppatori lottare è quando scegliere di utilizzare un'interfaccia o scegliere di utilizzare una classe astratta. Entrambi hanno vantaggi e svantaggi e il tempo e il luogo corretti per utilizzarli ciascuno. Ma come decidiamo ???

Entrambi prevedono il riutilizzo di funzionalità comuni tra i tipi. La differenza più evidente subito è che le interfacce non forniscono alcuna implementazione per la loro funzionalità mentre le classi astratte ti consentono di implementare un comportamento "base" o "predefinito" e quindi hanno la possibilità di "sovrascrivere" questo comportamento predefinito con i tipi derivati ​​dalle classi, se necessario .

Tutto questo va bene e prevede un grande riutilizzo del codice e aderisce al principio DRY (Don't Repeat Yourself) dello sviluppo del software. Le classi astratte sono fantastiche da usare quando si ha una relazione "è un".

Ad esempio: un golden retriever "è un" tipo di cane. Quindi è un barboncino. Entrambi possono abbaiare, come tutti i cani. Tuttavia, potresti voler affermare che il barboncino è significativamente diverso dalla corteccia di cane "predefinita". Pertanto, potrebbe avere senso implementare qualcosa come segue:

public abstract class Dog
{
      public virtual void Bark()
      {
        Console.WriteLine("Base Class implementation of Bark");
      }
}

public class GoldenRetriever : Dog
{
   // the Bark method is inherited from the Dog class
}

public class Poodle : Dog
{
  // here we are overriding the base functionality of Bark with our new implementation
  // specific to the Poodle class
  public override void Bark()
  {
     Console.WriteLine("Poodle's implementation of Bark");
  }
}

// Add a list of dogs to a collection and call the bark method.

void Main()
{
    var poodle = new Poodle();
    var goldenRetriever = new GoldenRetriever();

    var dogs = new List<Dog>();
    dogs.Add(poodle);
    dogs.Add(goldenRetriever);

    foreach (var dog in dogs)
    {
       dog.Bark();
    }
}

// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark

// 

Come puoi vedere, questo sarebbe un ottimo modo per mantenere il tuo codice DRY e consentire l'implementazione della classe di base quando uno qualsiasi dei tipi può semplicemente fare affidamento sul Bark predefinito invece di un'implementazione del caso speciale. Classi come GoldenRetriever, Boxer, Lab potrebbero ereditare gratuitamente la corteccia "default" (classe di basso) solo perché implementano la classe astratta Dog.

Ma sono sicuro che lo sapevi già.

Sei qui perché vuoi capire perché potresti voler scegliere un'interfaccia rispetto a una classe astratta o viceversa. Bene, uno dei motivi per cui potresti voler scegliere un'interfaccia rispetto a una classe astratta è quando non hai o vuoi impedire un'implementazione predefinita. Questo di solito è dovuto al fatto che i tipi che implementano l'interfaccia non sono correlati in una relazione "is a". In realtà, non devono essere affatto collegati, tranne per il fatto che ogni tipo "è in grado" o ha "l'abilità" di fare qualcosa o avere qualcosa.

Cosa diavolo significa? Bene, per esempio: un essere umano non è un'anatra ... e un'anatra non è un essere umano. Abbastanza ovvio. Tuttavia, sia un'anatra che un essere umano hanno "la capacità" di nuotare (dato che l'essere umano ha passato le sue lezioni di nuoto in prima elementare :)). Inoltre, poiché un'anatra non è un essere umano o viceversa, questa non è una "è una" realizzazione, ma una relazione "è capace" e possiamo usare un'interfaccia per illustrare che:

// Create ISwimable interface
public interface ISwimable
{
      public void Swim();
}

// Have Human implement ISwimable Interface
public class Human : ISwimable

     public void Swim()
     {
        //Human's implementation of Swim
        Console.WriteLine("I'm a human swimming!");
     }

// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
     public void Swim()
     {
          // Duck's implementation of Swim
          Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
     }
}

//Now they can both be used in places where you just need an object that has the ability "to swim"

public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
     somethingThatCanSwim.Swim();
}

public void Main()
{
      var human = new Human();
      var duck = new Duck();

      var listOfThingsThatCanSwim = new List<ISwimable>();

      listOfThingsThatCanSwim.Add(duck);
      listOfThingsThatCanSwim.Add(human);

      foreach (var something in listOfThingsThatCanSwim)
      {
           ShowHowYouSwim(something);
      }
}

 // So at runtime the correct implementation of something.Swim() will be called
 // Output:
 // Quack! Quack! I'm a Duck swimming!
 // I'm a human swimming!

L'uso di interfacce come il codice sopra ti permetterà di passare un oggetto in un metodo che "è in grado" di fare qualcosa. Al codice non importa come lo fa ... Tutto quello che sa è che può chiamare il metodo Swim su quell'oggetto e quell'oggetto saprà quale comportamento assumere in fase di esecuzione in base al suo tipo.

Ancora una volta, questo aiuta il tuo codice a rimanere ASCIUTTO in modo da non dover scrivere più metodi che chiamano l'oggetto per preformare la stessa funzione principale (ShowHowHumanSwims (umano), ShowHowDuckSwims (anatra), ecc.)

L'uso di un'interfaccia qui consente ai metodi di chiamata di non doversi preoccupare di quale tipo sia quale o come viene implementato il comportamento. Sa solo che, data l'interfaccia, ogni oggetto dovrà aver implementato il metodo Swim, quindi è sicuro chiamarlo nel proprio codice e consentire che il comportamento del metodo Swim sia gestito all'interno della propria classe.

Sommario:

Quindi la mia regola empirica principale è usare una classe astratta quando si desidera implementare una funzionalità "predefinita" per una gerarchia di classi o / e le classi o i tipi con cui si sta lavorando condividono una relazione "is a" (es. Barboncino "è un "Tipo di cane).

D'altra parte usa un'interfaccia quando non hai una relazione “è una” ma hai tipi che condividono “la capacità” di fare qualcosa o avere qualcosa (es. Anatra “non è” un essere umano. Tuttavia, anatra e condivisione umana "La capacità" di nuotare).

Un'altra differenza da notare tra le classi astratte e le interfacce è che una classe può implementare da una a molte interfacce ma una classe può ereditare solo da UNA classe astratta (o qualsiasi classe per quella materia). Sì, puoi nidificare le classi e avere una gerarchia di ereditarietà (che molti programmi hanno e dovrebbero avere) ma non puoi ereditare due classi in una definizione di classe derivata (questa regola si applica a C #. In alcune altre lingue puoi farlo, di solito solo a causa della mancanza di interfacce in queste lingue).

Ricorda anche quando usi le interfacce per aderire al principio di segregazione dell'interfaccia (ISP). L'ISP afferma che nessun client dovrebbe essere costretto a dipendere da metodi che non utilizza. Per questo motivo le interfacce dovrebbero essere focalizzate su compiti specifici e di solito sono molto piccole (es. IDisposable, IComparable).

Un altro suggerimento è se stai sviluppando piccole funzionalità concise, usa le interfacce. Se si progettano unità funzionali di grandi dimensioni, utilizzare una classe astratta.

Spero che questo chiarisca le cose per alcune persone!

Inoltre, se riesci a pensare a qualche esempio migliore o vuoi segnalare qualcosa, per favore fallo nei commenti qui sotto!


6

Il caso delle Classi di base sulle interfacce è stato ben spiegato nelle Linee guida per la codifica di Submain .NET:

Classi di base e interfacce Un tipo di interfaccia è una descrizione parziale di un valore, potenzialmente supportato da molti tipi di oggetti. Utilizzare le classi di base anziché le interfacce quando possibile. Dal punto di vista del controllo delle versioni, le classi sono più flessibili delle interfacce. Con una classe, è possibile spedire la versione 1.0 e quindi nella versione 2.0 aggiungere un nuovo metodo alla classe. Finché il metodo non è astratto, tutte le classi derivate esistenti continuano a funzionare invariate.

Poiché le interfacce non supportano l'ereditarietà dell'implementazione, il modello che si applica alle classi non si applica alle interfacce. L'aggiunta di un metodo a un'interfaccia equivale all'aggiunta di un metodo astratto a una classe base; qualsiasi classe che implementa l'interfaccia si interromperà perché la classe non implementa il nuovo metodo. Le interfacce sono appropriate nelle seguenti situazioni:

  1. Diverse classi non correlate vogliono supportare il protocollo.
  2. Queste classi hanno già classi di base stabilite (ad esempio, alcuni sono controlli dell'interfaccia utente (UI) e altri sono servizi Web XML).
  3. L'aggregazione non è appropriata o praticabile. In tutte le altre situazioni, l'eredità di classe è un modello migliore.

Sento che questa risposta dovrebbe attirare più attenzione. Va contro il grano di molte risposte, qui. Non direi di essere completamente d'accordo, ma ci sono grandi punti, qui.
kayleeFrye_onDeck

5

Una differenza importante è che puoi ereditare solo una classe base, ma puoi implementare molte interfacce. Quindi vuoi usare una classe base solo se sei assolutamente certo di non dover ereditare anche una classe base diversa. Inoltre, se ritieni che la tua interfaccia stia diventando grande, dovresti iniziare a cercare di suddividerla in alcuni elementi logici che definiscono funzionalità indipendenti, poiché non esiste alcuna regola che la tua classe non possa implementarli tutti (o che puoi definire un diverso interfaccia che li eredita tutti per raggrupparli).


4

Quando ho iniziato a studiare la programmazione orientata agli oggetti, ho commesso l'errore facile e probabilmente comune di usare l'ereditarietà per condividere un comportamento comune, anche quando quel comportamento non era essenziale per la natura dell'oggetto.

Per sviluppare ulteriormente un esempio molto usato in questa particolare domanda, ci sono molte cose che sono petable - fidanzate, macchine, coperte sfocate ... - quindi avrei potuto avere una classe Petable che forniva questo comportamento comune e varie classi che ereditano da.

Tuttavia, essere petable non fa parte della natura di nessuno di questi oggetti. Ci sono concetti molto più importanti che sono essenziali per la loro natura: la ragazza è una persona, la macchina è un veicolo terrestre, il gatto è un mammifero ...

I comportamenti devono essere assegnati prima alle interfacce (inclusa l'interfaccia predefinita della classe) e promossi a una classe base solo se sono (a) comuni a un grande gruppo di classi che sono sottoinsiemi di una classe più grande, nello stesso senso in cui "gatto" e "persona" sono sottoinsiemi di "mammifero".

Il problema è che, dopo aver capito il design orientato agli oggetti in modo sufficientemente migliore di quello che ho fatto all'inizio, normalmente lo farai automaticamente senza nemmeno pensarci. Quindi la nuda verità dell'affermazione "codice per un'interfaccia, non una classe astratta" diventa così ovvia che hai difficoltà a credere che qualcuno si preoccuperebbe di dirlo - e inizi a provare a leggere altri significati in esso.

Un'altra cosa che aggiungerei è che se una classe è puramente astratta - senza membri non astratti, non ereditati o metodi esposti a figlio, genitore o cliente - allora perché è una classe? Potrebbe essere sostituito, in alcuni casi da un'interfaccia e in altri casi da Null.


Una classe puramente astratta può fornire comportamenti predefiniti per i metodi. Questo è utile quando le tue classi concrete condivideranno tutti metodi comuni che sarebbero ridondanti da implementare ancora e ancora.
Adam Hughes

4

Preferisci le interfacce rispetto alle classi astratte

Razionale, i punti principali da considerare [due già menzionati qui] sono:

  • Le interfacce sono più flessibili, perché una classe può implementare più interfacce. Poiché Java non ha ereditarietà multiple, l'utilizzo di classi astratte impedisce agli utenti di utilizzare qualsiasi altra gerarchia di classi. In generale, preferisci le interfacce quando non ci sono implementazioni o stati predefiniti. Le raccolte Java offrono buoni esempi di ciò (Mappa, Set, ecc.).
  • Le classi astratte hanno il vantaggio di consentire una migliore compatibilità con il futuro. Una volta che i client utilizzano un'interfaccia, non è possibile cambiarla; se usano una classe astratta, puoi comunque aggiungere comportamento senza rompere il codice esistente. Se la compatibilità è un problema, considera l'utilizzo di classi astratte.
  • Anche se si dispone di implementazioni predefinite o stato interno, è consigliabile offrire un'interfaccia e un'implementazione astratta di essa . Ciò aiuterà i clienti, ma consentirà comunque loro maggiore libertà se lo desiderano [1].
    Naturalmente, l'argomento è stato discusso ampiamente altrove [2,3].

[1] Aggiunge più codice, ovviamente, ma se la brevità è la tua preoccupazione principale, probabilmente avresti dovuto evitare Java in primo luogo!

[2] Joshua Bloch, Effective Java, articoli 16-18.

[3] http://www.codeproject.com/KB/ar ...


3

I commenti precedenti sull'utilizzo di classi astratte per un'implementazione comune sono sicuramente in linea con gli altri. Un vantaggio che non ho ancora visto è che l'uso di interfacce rende molto più semplice implementare oggetti finti ai fini del test unitario. La definizione di IPet e PetBase come descritto da Jason Cohen consente di deridere facilmente diverse condizioni dei dati, senza l'overhead di un database fisico (fino a quando non si decide che è il momento di testare la cosa reale).


3

Non usare una classe base a meno che tu non sappia cosa significhi e che si applichi in questo caso. Se applicabile, usalo, altrimenti usa interfacce. Ma nota la risposta su piccole interfacce.

L'ereditarietà pubblica è sovrautilizzata in OOD ed esprime molto più di quanto la maggior parte degli sviluppatori realizzi o sia disposta a rispettare. Vedi il principio di sostituibilità di Liskov

In breve, se A "è un" B, allora A non richiede più di B e fornisce non meno di B, per ogni metodo che espone.


3

Un'altra opzione da tenere a mente è l'utilizzo della relazione "has-a", ovvero "implementata in termini di" o "composizione". A volte questo è un modo più pulito e flessibile di strutturare le cose rispetto all'uso dell'eredità "is-a".

Logicamente potrebbe non avere molto senso dire che sia Dog che Cat "hanno" un animale domestico, ma evita insidie ​​ereditarie multiple comuni:

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Sì, questo esempio mostra che vi è molta duplicazione del codice e mancanza di eleganza nel fare le cose in questo modo. Ma si dovrebbe anche apprezzare che questo aiuta a mantenere disaccoppiato Dog e Cat dalla classe Pet (in quanto Dog and Cat non hanno accesso ai membri privati ​​di Pet), e lascia spazio a Dog and Cat per ereditare da qualcos'altro- forse la classe dei mammiferi.

La composizione è preferibile quando non è richiesto alcun accesso privato e non è necessario fare riferimento a Dog and Cat utilizzando riferimenti / puntatori Pet generici. Le interfacce ti offrono quella capacità di riferimento generica e possono aiutare a ridurre la verbosità del tuo codice, ma possono anche offuscare le cose quando sono mal organizzate. L'ereditarietà è utile quando hai bisogno dell'accesso di un membro privato e, nell'usarla, ti impegni ad accoppiare fortemente le tue classi Dog e Cat alla tua classe Pet, che è un costo elevato da pagare.

Tra eredità, composizione e interfacce non esiste un modo che sia sempre giusto e aiuta a considerare come tutte e tre le opzioni possano essere utilizzate in armonia. Dei tre, l'ereditarietà è in genere l'opzione che dovrebbe essere utilizzata il meno spesso.


3

Concettualmente, un'interfaccia viene utilizzata per definire formalmente e semi-formalmente un insieme di metodi che un oggetto fornirà. Formalmente significa un insieme di nomi e firme di metodi e semi-formalmente significa documentazione leggibile dall'uomo associata a tali metodi.

Le interfacce sono solo descrizioni di un'API (dopotutto, API sta per interfaccia di programmazione dell'applicazione ), non possono contenere alcuna implementazione e non è possibile utilizzare o eseguire un'interfaccia. Rendono esplicito il contratto su come interagire con un oggetto.

Le classi forniscono un'implementazione e possono dichiarare di implementare zero, una o più interfacce. Se si intende ereditare una classe , la convenzione prevede il prefisso del nome della classe con "Base".

Esiste una distinzione tra una classe base e una classe base astratta (ABC). Gli ABC mescolano insieme interfaccia e implementazione. Estratto al di fuori della programmazione per computer significa "sommario", ovvero "abstract == interfaccia". Una classe base astratta può quindi descrivere sia un'interfaccia, sia un'implementazione vuota, parziale o completa che si intende ereditare.

Le opinioni su quando utilizzare le interfacce rispetto alle classi di base astratte rispetto alle sole classi varieranno in modo selvaggio in base sia a ciò che si sta sviluppando, sia al linguaggio in cui si sta sviluppando. Le interfacce sono spesso associate solo a linguaggi tipizzati staticamente come Java o C #, ma i linguaggi tipizzati dinamicamente possono anche avere interfacce e classi base astratte . In Python per esempio, la distinzione netta tra una classe, che dichiara che implementa un'interfaccia , e un oggetto, che è un esempio di una classe , e si dice che fornire che interfaccia. In un linguaggio dinamico è possibile che due oggetti che sono entrambi istanze della stessa classe possano dichiarare che forniscono interfacce completamente diverse . In Python questo è possibile solo per gli attributi degli oggetti, mentre i metodi sono stati condivisi tra tutti gli oggetti di una classe . Tuttavia, in Ruby, gli oggetti possono avere metodi per istanza, quindi è possibile che l' interfaccia tra due oggetti della stessa classe possa variare quanto desidera il programmatore (tuttavia, Ruby non ha alcun modo esplicito di dichiarare le interfacce).

Nei linguaggi dinamici spesso si presume implicitamente l'interfaccia con un oggetto, sia introspettendo un oggetto e chiedendogli quali metodi fornisce ( prima di saltare ) o preferibilmente semplicemente tentando di usare l' interfaccia desiderata su un oggetto e catturando le eccezioni se l'oggetto non prevede che l'interfaccia ( più facile chiedere perdono che il permesso ). Ciò può portare a "falsi positivi" in cui due interfacce hanno lo stesso nome di metodo, ma sono semanticamente diverse. Tuttavia, il compromesso è che il tuo codice è più flessibile poiché non è necessario specificare anticipatamente per anticipare tutti i possibili usi del codice.


2

Dipende dalle vostre esigenze. Se IPet è abbastanza semplice, preferirei implementarlo. Altrimenti, se PetBase implementa una tonnellata di funzionalità che non vuoi duplicare, allora ce l'hai.

L'aspetto negativo di implementare una classe base è il requisito override(o new) dei metodi esistenti. Questo li rende metodi virtuali, il che significa che devi stare attento a come usi l'istanza dell'oggetto.

Infine, la singola eredità di .NET mi uccide. Un esempio ingenuo: supponi di avere il controllo dell'utente, quindi erediti UserControl. Ma ora sei bloccato anche dall'ereditare PetBase. Questo ti costringe a riorganizzare, ad esempio per creare un PetBasemembro della classe.


2

Di solito non implemento neanche fino a quando non ne ho bisogno. Preferisco le interfacce rispetto alle classi astratte perché offrono un po 'più di flessibilità. Se c'è un comportamento comune in alcune delle classi ereditarie, lo sposto in alto e creo una classe base astratta. Non vedo la necessità di entrambi, dal momento che essenzialmente servono allo stesso scopo, e avere entrambi è un cattivo odore di codice (imho) che la soluzione è stata ingegnerizzata.


2

Per quanto riguarda C #, in alcuni sensi le interfacce e le classi astratte possono essere intercambiabili. Tuttavia, le differenze sono: i) le interfacce non possono implementare il codice; ii) per questo motivo, le interfacce non possono richiamare ulteriormente lo stack nella sottoclasse; e iii) solo una classe astratta può essere ereditata su una classe, mentre su una classe possono essere implementate più interfacce.


2

Per impostazione predefinita, l'interfaccia fornisce un livello per comunicare con altri codici. Tutte le proprietà e i metodi pubblici di una classe implementano di default l'interfaccia implicita. Possiamo anche definire un'interfaccia come un ruolo, quando ogni classe deve svolgere quel ruolo, deve implementarla dandogli diverse forme di implementazione a seconda della classe che la implementa. Quindi quando parli di interfaccia, stai parlando di polimorfismo e quando parli di classe base, stai parlando di eredità. Due concetti di oops !!!


2

Ho scoperto che un modello di Interfaccia> Astratto> Calcestruzzo funziona nel seguente caso d'uso:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

La classe astratta definisce gli attributi condivisi predefiniti delle classi concrete, ma applica l'interfaccia. Per esempio:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

Ora, poiché tutti i mammiferi hanno i capelli e i capezzoli (AFAIK, non sono uno zoologo), possiamo portarli nella classe base astratta

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

E poi le classi concrete semplicemente definiscono che camminano dritte.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

Questo design è bello quando ci sono molte classi concrete e non vuoi mantenere la caldaia solo per programmare su un'interfaccia. Se si aggiungessero nuovi metodi all'interfaccia, si romperanno tutte le classi risultanti, quindi si stanno ancora ottenendo i vantaggi dell'approccio dell'interfaccia.

In questo caso, l'abstract potrebbe anche essere concreto; tuttavia, la designazione astratta aiuta a sottolineare che questo modello viene utilizzato.


1

Un erede di una classe base dovrebbe avere una relazione "is a". L'interfaccia rappresenta una relazione "implementa una". Quindi usa una classe base solo quando i tuoi eredi manterranno la relazione.


1

Utilizzare le interfacce per applicare un contratto ACROSS alle famiglie di classi non correlate. Ad esempio, potresti avere metodi di accesso comuni per le classi che rappresentano raccolte, ma contengono dati radicalmente diversi, ad esempio una classe potrebbe rappresentare un set di risultati da una query, mentre l'altra potrebbe rappresentare le immagini in una galleria. Inoltre, puoi implementare più interfacce, permettendoti così di fondere (e significare) le capacità della classe.

Usa Ereditarietà quando le classi hanno una relazione comune e quindi hanno una firma strutturale e comportamentale simile, ad esempio auto, moto, camion e SUV sono tutti i tipi di veicoli stradali che potrebbero contenere un numero di ruote, una velocità massima

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.