Qual è la differenza tra un'interfaccia e una classe e perché dovrei usare un'interfaccia quando posso implementare i metodi direttamente nella classe?


118

Sono consapevole che questa è una domanda molto semplice, ma un intervistatore me l'ha posta in un modo molto astuto ed ero impotente :(

Conosco solo la definizione materiale o teorica di un'interfaccia e l'ho anche implementata in molti progetti su cui ho lavorato. Ma davvero non capisco perché e come sia utile.

Inoltre non capisco una cosa nell'interfaccia. ad esempio, usiamo

conn.Dispose();in finalmente blocco. Ma non vedo che la classe sta implementando o ereditando la classe IDisposableinterface ( SqlConnection), intendo. Mi chiedo come posso semplicemente chiamare il nome del metodo. Inoltre, nella stessa cosa, non capisco come funziona il metodo Dispose perché, abbiamo bisogno di implementare il corpo della funzione con la nostra implementazione per tutti i metodi di interfaccia. Allora come vengono accettate o denominate le interfacce come contratti? Queste domande hanno continuato a circolare nella mia mente fino ad ora e, francamente, non ho mai visto un buon filo che spiegasse le mie domande in un modo che io possa capire.

MSDN come al solito sembra molto spaventoso e nessuna singola riga è chiara lì ( gente, scusa gentilmente chi è nello sviluppo di alto livello, sento fortemente che qualsiasi codice o articolo dovrebbe raggiungere la mente di chiunque lo veda, quindi come molti altri dicono, MSDN non è utile ).

L'intervistatore ha detto:

Ha 5 metodi ed è felice di implementarlo direttamente nella classe, ma se devi andare per la classe o l'interfaccia Abstract, quale scegli e perché? Gli ho risposto a tutte le cose che ho letto in vari blog dicendo vantaggi e svantaggi sia della classe astratta che dell'interfaccia, ma non è convinto, sta cercando di capire "Perché l'interfaccia" in generale. "Perché classe astratta" in generale anche se posso implementare gli stessi metodi solo una volta e non gona cambiarlo.

Non vedo dove in rete, potrei ottenere un articolo che mi spiegherebbe chiaramente le interfacce e il suo funzionamento. Sono uno di quei tanti programmatori che ancora non conoscono le interfacce (conosco la teoria e i metodi che ho usato) ma non sono convinto di averlo capito chiaramente.


4
Anche le interfacce sono quelle che ho faticato a capire. Buona domanda.
Brian

4
programmare un contratto astratto piuttosto che un'implementazione concreta ... In breve, significa che puoi sostituire qualsiasi oggetto che implementa un'interfaccia quando è richiesta un'interfaccia.
Mitch Wheat

7
SqlConnectioneredita System.ComponentModel.Componentquale implementa IDisposable.
Lee

2
@ MitchWheat - Non vuole essere un esempio, la domanda chiede come SqlConnectionimplementa IDisposable.
Lee

Oh Lee, questo mi ha fatto capire grazie. Ma ancora non vedo come o dove sia definita la funzionalità del metodo "Dispose".
Studente

Risposte:


92

Le interfacce sono eccellenti quando vuoi creare qualcosa di simile:

using System;

namespace MyInterfaceExample
{
    public interface IMyLogInterface
    {
        //I want to have a specific method that I'll use in MyLogClass
        void WriteLog();       
    }

    public class MyClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyClass was Logged");
        }
    }

    public class MyOtherClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyOtherClass was Logged");
            Console.Write("And I Logged it different, than MyClass");
        }
    }

    public class MyLogClass
    {
        //I created a WriteLog method where I can pass as a parameter any object that implements IMyLogInterface.
        public static void WriteLog(IMyLogInterface myLogObject)
        {
            myLogObject.WriteLog(); //So I can use WriteLog here.
        }
    }

    public class MyMainClass
    {
        public void DoSomething()
        {
            MyClass aClass = new MyClass();
            MyOtherClass otherClass = new MyOtherClass();

            MyLogClass.WriteLog(aClass);//MyClass can log, and have his own implementation
            MyLogClass.WriteLog(otherClass); //As MyOtherClass also have his own implementation on how to log.
        }
    }
}

Nel mio esempio, potrei essere uno sviluppatore che scrive MyLogClasse gli altri sviluppatori potrebbero creare le loro classi e, quando volevano accedere, implementano l'interfaccia IMyLogInterface. È come se mi chiedessero cosa devono implementare per utilizzare il WriteLog()metodo in MyLogClass. La risposta che troveranno nell'interfaccia.


3
Ehi, questo mi sembra un ottimo ingrediente da capire, lo apprezzo davvero, grazie mille :) :)
Studente

14
La mia domanda è se stai creando un'istanza MyClasse MyOtherClassperché non dovresti semplicemente chiamare aClass.WriteLog()perché aggiungi quel passaggio extra. L'implementazione di WriteLog()rimarrebbe diversa per ciascuna delle classi ma hai già l'oggetto, quindi perché passarlo a una classe gestore?
Zach M.

Hm potrebbe essere che se metti il ​​tuo esempio di logging su nugget sarebbe più semplice per gli altri usare il tuo logger, senza conoscerne i dettagli .. ma d'altra parte, ancora non è una classe universale, (e potrei scrivere un interfaccia con i livelli di registrazione AND allert) sono ancora solo all'interno del tuo ambito. quindi oltre a te per chi ne trae vantaggio?
user3800527

2
@ZachM. Se ho ragione, la risposta significa che non istanzerà le classi, ma altri sviluppatori creeranno un'istanza delle classi e la passeranno come parametro al MyLogClass WriteLogmetodo. Quindi il suo metodo può gestire qualsiasi oggetto che implementa IMyLogInterface. Ecco un altro post interessante.
shaijut

1
La mia domanda è perché Interface ??? Lo scenario sopra può essere ottenuto anche da una classe astratta con tutti i metodi astratti.
Abhijit Ojha

52

Uno dei motivi per cui utilizzo le interfacce è perché aumenta la flessibilità del codice. Supponiamo di avere un metodo che accetta un oggetto di tipo di classe Account come parametro, ad esempio:

public void DoSomething(Account account) {
  // Do awesome stuff here.
}

Il problema con questo è che il parametro del metodo è fissato verso un'implementazione di un account. Va bene se non avresti mai bisogno di nessun altro tipo di account. Prendi questo esempio, che invece utilizza un'interfaccia account come parametro.

public void DoSomething(IAccount account) {
  // Do awesome stuff here.
}

Questa soluzione non è fissata per un'implementazione, il che significa che posso passarle un SuperSavingsAccount o un ExclusiveAccount (entrambi implementano l'interfaccia IAccount) e ottenere un comportamento diverso per ogni account implementato.


45

Le interfacce sono contratti che gli implementatori devono seguire. Le classi astratte consentono contratti più implementazioni condivise - qualcosa che le interfacce non possono avere. Le classi possono implementare ed ereditare più interfacce. Le classi possono estendere solo una singola classe astratta.

Perché Interface

  • Non disponi di un'implementazione del codice predefinita o condivisa
  • Desideri condividere i contratti dati (servizi web, SOA)
  • Hai diverse implementazioni per ogni implementatore dell'interfaccia (che IDbCommandha SqlCommande OracleCommandche implementa l'interfaccia in modi specifici )
  • Vuoi supportare l'ereditarietà multipla .

Perché Abstract


2
@ Silver Ho letto la maggior parte di quello che hai digitato nei blog, ma sto cercando di capire praticamente. Ho realizzato servizi WCF, interfacce esposte (ma era solo una singola app stand alone senza upstream o downstream). Quindi non riuscivo a capirlo correttamente anche se avevo interfacce progettate e implementate molto bene. La mia domanda è, praticamente, condividi semplicemente il significato del contratto dei nomi dei metodi, giusto? COME È UTILE :( So che costringe solo a implementare tutti i metodi, ma altrimenti come? Nel tuo post sopra sull'interfaccia, il 2 ° punto dice condivisione, significa che puoi fornire un esempio pratico in tempo reale di questo
Studente

1
Per un esempio pratico relativo alle interfacce e alla SOA, condividiamo le nostre interfacce WCF ( DataContracts) in un assembly .NET ( ad esempio Contracts.Shared.dll ) in modo che i consumatori del client .NET possano interagireChannelFactory facilmente utilizzando ( evitando di generare codice tramite Aggiungi riferimento al servizio, ecc. ) o utilizzando Aggiungi riferimento al servizio con tipi condivisi
SliverNinja - MSFT

Se, dichiarerò solo metodi astratti all'interno di un calss astratto, la classe astratta fungerà da interfaccia, allora perché abbiamo bisogno dell'interfaccia?
Abhijit Ojha

25

inserisci qui la descrizione dell'immagine

Quindi, in questo esempio, PowerSocket non sa nient'altro sugli altri oggetti. Gli oggetti dipendono tutti dall'alimentazione fornita dal PowerSocket, quindi implementano IPowerPlug e così facendo possono connettersi ad esso.

Le interfacce sono utili perché forniscono contratti che gli oggetti possono utilizzare per lavorare insieme senza bisogno di sapere nient'altro l'uno dell'altro.


Questo ha senso, ma sto ancora lottando per capire, potresti semplicemente non creare una classe base per PowerSocket e tutte quelle altre cose ereditarla se necessario. Tecnicamente i Power Sockets non hanno idea delle altre classi.
altaaf.hussein

Penso perché l'ereditarietà multipla non è consentita in C #
Hugh Seagraves il

22

In una parola: a causa del polimorfismo !

Se si "programma su un'interfaccia, non su un'implementazione", è possibile iniettare oggetti diversi che condividono la stessa interfaccia (tipo) nel metodo come argomento. In questo modo il codice del tuo metodo non è accoppiato con alcuna implementazione di un'altra classe, il che significa che è sempre aperto a lavorare con oggetti appena creati della stessa interfaccia. (Principio di apertura / chiusura)

  • Guarda in Dependency Injection e leggi decisamente Design Patterns - Elements of Reusable Object-Oriented Software di GOF.

4

C # non ha la digitazione a papera - solo perché sai che un certo metodo è implementato in un insieme di classi concrete non significa che puoi trattarle allo stesso modo per quanto riguarda la chiamata a quel metodo. L'implementazione di un'interfaccia consente di trattare tutte le classi che la implementano come lo stesso tipo di cose, rispetto a ciò che definisce l'interfaccia.


3
Puoi ottenere una sorta di ducktyping in .net4 con il tipo dinamico.
Tony Hopkinson

4

Credo che molto sangue sia già stato versato nel porre queste domande, e molti cercano di risolvere questi problemi spiegando termini simili a robot che nessun essere umano normale può capire.

Quindi prima. per capire perché l'interfaccia e perché astratta è necessario imparare a cosa servono. Personalmente ho imparato questi due quando ho applicato Factory Class. trovi un buon tuturial su questo link

Ora scaviamo in base al link che ho già fornito.

Hai la classe del veicolo che potrebbe cambiare in base alle esigenze dell'utente (come l'aggiunta di camion , carro armato , aereo , ecc. E dato che abbiamo

public class clsBike:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Bike");
    }
    #endregion
}

e

public class clsCar:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Car");
    }
    #endregion
}

ed entrambi hanno contratto IChoice che dice semplicemente che la mia classe dovrebbe avere il metodo di acquisto

public interface IChoice
{
    string Buy();
}

Ora, vedete, quell'interfaccia impone solo il metodo, Buy()ma lascia che la classe ereditata decida cosa fare quando lo implementa. Questa è la limitazione dell'interfaccia, usando esclusivamente l'interfaccia, potresti finire per ripetere alcune attività che possiamo implementare automaticamente usando abstact. Nel nostro esempio diciamo che l'acquisto di ogni veicolo ha uno sconto.

public abstract class Choice
{
    public abstract string Discount { get; }
    public abstract string Type { get; }
    public string Buy()
    {
       return "You buy" + Type + " with " + Discount;
}
public class clsBike: Choice
{
    public abstract string Discount { get { return "10% Discount Off"; } }
    public abstract string Type { get { return "Bike"; } }
}

public class clsCar:Choice
{
    public abstract string Discount { get { return " $15K Less"; } }
    public abstract string Type { get { return "Car"; } }
}

Ora usando la classe Factory, puoi ottenere la stessa cosa ma usando abstract, lasci che la classe base esegua il Buy()metodo.

In sintesi: i contratti di interfaccia consentono alla classe inherit di eseguire l'implementazione mentre i contratti di classe astratta possono inizializzare l'implementazione (che può sovrascrivere dalla classe Inherit)


2

Con un'interfaccia puoi fare quanto segue:

1) Crea interfacce separate che offrono diversi tagli della tua implementazione, consentendo un'interfaccia più coesa.

2) Consenti più metodi con lo stesso nome tra le interfacce, perché ehi, non hai implementazioni in conflitto, solo una firma.

3) È possibile eseguire la versione e l'hive della propria interfaccia indipendentemente dalla propria implementazione, assicurandosi che un contratto venga rispettato.

4) Il tuo codice può fare affidamento sull'astrazione piuttosto che sulla concrezione, consentendo l'iniezione intelligente delle dipendenze, inclusa l'iniezione di mock di test ecc.

Ci sono molte altre ragioni, ne sono sicuro, queste sono solo alcune.

Una classe astratta ti consente di avere una base parzialmente concreta su cui lavorare, questa non è la stessa di un'interfaccia ma ha le sue qualità come la capacità di creare un'implementazione parziale utilizzando il modello del metodo modello.


Hai trascurato la cosa più importante: l'implementazione di un'interfaccia rende la tua classe utilizzabile da qualsiasi codice che necessiti di un'implementazione di quell'interfaccia, senza che il codice in questione debba sapere nulla della tua classe.
supercat

2

Come un esempio reale della risposta @ user2211290:

Entrambi Arraye Listhanno interfaccia IList. Di seguito abbiamo un string[]e un List<string>e li controlliamo entrambi con un metodo utilizzando IList :

string[] col1 = { "zero", "one", "two", "three", "four"};
List<string> col2 = new List<string>{ "zero", "one", "two", "three"};

//a methode that manipulates both of our collections with IList
static void CheckForDigit(IList collection, string digit)
{
    Console.Write(collection.Contains(digit));
    Console.Write("----");
    Console.WriteLine(collection.ToString()); //writes the type of collection
}

static void Main()
{
    CheckForDigit(col1, "one");   //True----System.String[]
    CheckForDigit(col2, "one");   //True----System.Collections.Generic.List`1[System.String]



//Another test:

    CheckForDigit(col1, "four");   //True----System.String[]
    CheckForDigit(col2, "four");   //false----System.Collections.Generic.List`1[System.String]
}

1

Puoi ereditare solo da una classe astratta. Puoi ereditare da più interfacce. Questo determina cosa uso per la maggior parte dei casi.

Il vantaggio della classe astratta sarebbe che puoi avere un'implementazione di base. Tuttavia, nel caso di IDisposable, un'implementazione predefinita è inutile, poiché la classe base non sa come pulire correttamente le cose. Quindi, un'interfaccia sarebbe più adatta.


1

Sia la classe astratta che l'interfaccia sono contratti.

L'idea di un contratto è che tu specifichi un comportamento. Se dici di aver implementato hai accettato il contratto.

La scelta dell'astratto sull'interfaccia è.

Qualsiasi discendente non astratto della classe abstract implementerà il contratto.

contro

Qualsiasi classe che implementa l'interfaccia implementerà il contratto.

Quindi usi abstract quando vuoi specificare un comportamento che tutti i discendenti devono implementare e salva te stesso definendo un'interfaccia separata, ma ora tutto ciò che soddisfa questo contratto effettivamente aggregato deve essere un discendente.


1

Le interfacce devono fare un'astrazione (un archetipo) dell'astrazione (le classi) della realtà (gli oggetti).

Le interfacce devono specificare i termini del contratto senza fornire l'implementazione fornita dalle classi.

Le interfacce sono specifiche:

  • Le interfacce sono artefatti della fase di progettazione per specificare il comportamento immobile del concetto in quanto è solo e statico.

  • Le classi sono artefatti del tempo di implementazione per specificare la struttura mobile della realtà mentre interagisce e si muove.

Cos'è un'interfaccia?

Quando osservi un gatto puoi dire che è un animale che ha quattro zampe, una testa, un tronco, una coda e un pelo. Puoi vedere che può camminare, correre, mangiare e miagolare. E così via.

Hai appena definito un'interfaccia con le sue proprietà e le sue operazioni. In quanto tale non hai definito alcun modus operandi, ma solo caratteristiche e capacità senza sapere come funzionano le cose: hai definito abilità e distinzioni.

In quanto tale, non è ancora una vera classe, anche se in UML la chiamiamo classe in un diagramma delle classi perché possiamo definire membri privati ​​e protetti per iniziare ad avere una visione approfondita del manufatto. Non essere confuso qui perché in UML un'interfaccia è una cosa leggermente diversa da un'interfaccia in C #: è come un punto di accesso parziale all'atomo di astrazione. Come tale abbiamo detto che una classe può implementare più interfacce. In quanto tale è la stessa cosa, ma non, perché le interfacce in C # vengono utilizzate sia per astrarre l'astrazione che per limitare questa astrazione come punto di accesso. Sono due usi diversi. Quindi una classe in UML rappresenta un'interfaccia di accoppiamento completo a una classe di programmazione, mentre un'interfaccia UML rappresenta un'interfaccia di disaccoppiamento di una sezione di una classe di programmazione. Infatti, il diagramma delle classi in UML non si occupa dell'implementazione e tutti i suoi artefatti sono a livello di interfaccia di programmazione. Sebbene associamo le classi UML alle classi di programmazione, si tratta di una trasposizione dell'astrazione astratta in astrazione concreta. C'è una sottigliezza che spiega la dicotomia tra il campo del design e il campo della programmazione. Quindi una classe in UML è una classe di programmazione dal punto di vista di un'interfaccia di programmazione considerando le cose nascoste interne.

Le interfacce consentono anche di simulare l'ereditarietà multipla quando non sono disponibili in modo scomodo. Ad esempio, la classe cat implementerà l'interfaccia cat che deriva dall'interfaccia animale. Questa classe di gatti implementerà anche queste interfacce: cammina, corri, mangia e fai un suono. Questo compensa l'assenza di eredità multipla a livello di classe, ma ogni volta è necessario reimplementare tutto e non è possibile fattorizzare la realtà al meglio come la realtà stessa fa.

Per capirlo si può fare riferimento alla codifica Pascal Object dove si definiscono in un'unità l'interfaccia e le sezioni di implementazione. Nell'interfaccia si definiscono i tipi e nell'implementazione si implementa il tipo:

unit UnitName;

interface

type
  TheClass = class
  public
    procedure TheMethod;
  end;

implementation

class procedure TheClass.TheMethod;
begin
end;

Qui, la sezione dell'interfaccia corrisponde al design della classe UML mentre i tipi di interfacce sono quindi altre cose.

Quindi nella nostra attività abbiamo una parola, interfaccia , per nominare due cose distinte ma simili, ed è fonte di confusione.

Anche in C #, ad esempio, le interfacce di programmazione permettono di compensare l'assenza di un vero polimorfismo generico sui tipi aperti senza riuscire veramente a raggiungere l'obiettivo perché si perde la capacità fortemente tipizzata.

Dopotutto, le interfacce sono necessarie per consentire a sistemi incompatibili di comunicare senza preoccuparsi dell'implementazione e della gestione degli oggetti in memoria come introdotto con il Common Object Model (Distribuito).

Cos'è una classe?

Dopo aver definito una riduzione della realtà da un punto di vista esterno, puoi quindi descriverla da una prospettiva interna: questa è la classe in cui definisci l'elaborazione dei dati e la gestione dei messaggi per permettere alla realtà che hai incapsulato di prendere vita e interagire grazie agli oggetti utilizzando le istanze.

Quindi in UML realizzi un'immersione frattale nelle ruote della macchina e descrivi gli stati, le interazioni e così via per poter implementare l'astrazione del frammento della realtà che vuoi gestire.

In quanto tale, una classe astratta è in qualche modo l'equivalente di un'interfaccia dal punto di vista del compilatore.

Maggiori informazioni

Protocollo (programmazione orientata agli oggetti)

C # - Interfacce

C # - Classi


1

Lascia che ti parli dei tostapane volanti.

tostapane volante

Ci sono, naturalmente, molte situazioni in cui è possibile costruire un sistema software funzionante senza dichiarare o implementare alcuna interfaccia: qualsiasi progetto di software orientato agli oggetti può essere realizzato utilizzando nient'altro che classi.

Inoltre, qualsiasi sistema software può essere implementato anche in linguaggio Assembly, o meglio ancora in codice macchina. Il motivo per cui utilizziamo meccanismi di astrazione è perché tendono a rendere le cose più facili. Le interfacce sono un tale meccanismo di astrazione.

Quindi, accade solo che ci siano alcuni progetti orientati agli oggetti non banali che sono molto più facili da implementare se si usano le interfacce, che le interfacce diventano praticamente necessarie in quei casi.

Questi progetti non banali hanno a che fare con l'ereditarietà multipla, che, nella sua forma "vera", è quando una classe eredita non solo da una classe base, ma da due o più classi base. Questa vera forma non è possibile in C #, ma prima che esistessero linguaggi come C # e Java, il linguaggio che governava era il C ++, che supporta completamente la vera ereditarietà multipla. Sfortunatamente, la vera eredità multipla si è rivelata non essere una buona idea, perché complica immensamente il design del linguaggio, e dà anche origine a vari problemi, ad esempio il famoso "Diamond Problem". (Vedi "Qual è il problema esatto con l'ereditarietà multipla?" Risposta di J Francis )

Quindi, se qualcuno volesse costruire una classe "tostapane volante", erediterebbe da qualche classe "tostapane" esistente e anche da una classe "volante" esistente. Il tipo di problema in cui probabilmente si sarebbero imbattuti era che l'alimentatore della classe del tostapane era probabilmente una presa a muro, mentre l'alimentatore della classe della macchina volante era probabilmente il cibo per piccioni, e la nuova classe risultante sarebbe stata in qualche modo hanno entrambi, o non sarebbe chiaro quale avrebbe. (Il problema del diamante.)

I creatori di linguaggi come C # e Java hanno deciso di non consentire la vera ereditarietà multipla, al fine di mantenere il linguaggio semplice ed evitare insidie ​​come il Diamond Problem. Tuttavia, una qualche forma di ereditarietà multipla è ancora necessaria (o almeno molto altamente desiderabile), quindi in questi linguaggi hanno introdotto le interfacce come mezzo per supportare una forma minore di ereditarietà multipla evitando i problemi e la complessità della vera eredità multipla.

In questa forma minore di ereditarietà multipla, non ti è permesso avere una classe che eredita da più di una classe base, ma puoi almeno ereditare da una o più interfacce. Quindi, se vuoi costruire un tostapane volante, non puoi ereditare sia da una classe di tostapane esistente sia da una classe di volo esistente, ma quello che puoi fare è ereditare da una classe di tostapane esistente e quindi esporre anche un'interfaccia volante che implementerai tu stesso, possibilmente usando qualsiasi mezzo che hai già ereditato dal tostapane.

Quindi, a meno che tu non senta mai la necessità di creare una classe che aggreghi due set di funzionalità diversi e non correlati, non avrai bisogno di alcuna forma di ereditarietà multipla, quindi non avrai bisogno di dichiarare, o implementare, alcuna interfaccia.


0

Le interfacce consentono al progettista di classi di rendere i metodi disponibili molto chiari per l'utente finale. Sono anche parte integrante del polimorfismo.


Ben detto nella tua prima dichiarazione. Ma non ho capito la tua seconda affermazione, potresti elaborare con un esempio in tempo reale per favore?
Studente

0

Non posterò la definizione di un'interfaccia rispetto a una classe astratta perché penso che tu conosca molto bene la teoria e presumo che tu conosca i principi SOLID, quindi mettiamoci in pratica.

Come sapete le interfacce non possono avere alcun codice, quindi lo svantaggio è abbastanza semplice da capire.

se hai bisogno di inizializzare la proprietà della tua classe fornendo un costruttore o se vuoi fornire parte dell'implementazione, una classe astratta si adatterebbe bene a un'interfaccia che non ti permetterebbe di farlo.

Quindi in generale dovresti preferire la classe astratta alle interfacce quando devi fornire un costruttore o qualsiasi codice al client che erediterà / estenderà la tua classe


-2

Le classi astratte sono create per entità correlate dove as Interfaces può essere utilizzato per entità non correlate.

Ad esempio, se ho due entità che dicono Animale e Umano, andrò per Interfaccia dove come se dovessi andare in dettaglio dire Tigre, leone e voglio relazionarmi con Animale, sceglierò la classe Animal Abstract.

apparirà come sotto

   Interface             
   ____|____
  |        |
Animal   Human



  Animal (Abstract class)
   __|___
  |      |
Tiger   Lion
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.