Perché non riesco a capire le interfacce?

Qualcuno potrebbe per favore demistificare le interfacce per me o indicarmi qualche buon esempio? Continuo a vedere le interfacce popup qua e là, ma non sono mai stato veramente esposto a buone spiegazioni di interfacce o quando usarle.

Sto parlando di interfacce in un contesto di interfacce rispetto a classi astratte.

Le interfacce ti consentono di programmare una “descrizione” anziché un tipo, che ti consente di associare più liberamente gli elementi del tuo software.

Pensala in questo modo: vuoi condividere i dati con qualcuno nel cubo accanto a te, quindi estrai la tua penna flash e copia / incolla. Cammini nella porta accanto e il tizio dice “è quella USB?” e tu dici di sì – tutto pronto. Non importa la dimensione della penna flash, né il produttore – tutto ciò che conta è che è USB.

Allo stesso modo, le interfacce ti consentono di generare il tuo sviluppo. Usando un’altra analogia, immagina di voler creare un’applicazione che dipingesse virtualmente le automobili. Potresti avere una firma come questa:

public void Paint(Car car, System.Drawing.Color color)... 

Questo funzionerebbe fino a quando il tuo cliente ha detto “ora voglio dipingere camion” in modo da poter fare questo:

 public void Paint (Vehicle vehicle, System.Drawing.Color color)... 

questo amplierebbe la tua app … fino a quando il tuo cliente ha detto “ora voglio dipingere le case!” Ciò che avresti potuto fare sin dall’inizio è creare un’interfaccia:

 public interface IPaintable{ void Paint(System.Drawing.Color color); } 

… e lo hai passato alla tua routine:

 public void Paint(IPaintable item, System.Drawing.Color color){ item.Paint(color); } 

Spero che questo abbia un senso: è una spiegazione piuttosto semplicistica, ma si spera che arrivi al cuore.

Le interfacce stabiliscono un contratto tra una class e il codice che la chiama. Inoltre ti permettono di avere classi simili che implementano la stessa interfaccia ma fanno azioni o eventi diversi e non devono sapere con chi stai effettivamente lavorando. Questo potrebbe avere più senso come esempio, quindi fammi provare uno qui.

Supponi di avere un paio di classi chiamate Cane, Gatto e Topo. Ognuna di queste classi è un animale domestico e in teoria potresti ereditarle tutte da un’altra class chiamata Pet ma ecco il problema. Gli animali da soli non fanno nulla. Non puoi andare al negozio e comprare un animale domestico. Puoi andare a comprare un cane o un gatto ma un animale domestico è un concetto astratto e non concreto.

Quindi sai che gli animali domestici possono fare certe cose. Possono dormire, o mangiare, ecc. Quindi definisci un’interfaccia chiamata IPet e sembra qualcosa del genere (syntax C #)

 public interface IPet { void Eat(object food); void Sleep(int duration); } 

Ciascuna delle classi Dog, Cat e Mouse implementa IPet.

 public class Dog : IPet 

Quindi ora ciascuna di queste classi deve avere la propria implementazione di Eat e Sleep. Yay hai un contratto … Ora qual è il punto.

Diciamo che vuoi creare un nuovo object chiamato PetStore. E questo non è un ottimo PetStore, quindi fondamentalmente ti vendono solo un animale domestico a caso (sì, so che questo è un esempio forzato).

 public class PetStore { public static IPet GetRandomPet() { //Code to return a random Dog, Cat, or Mouse } } IPet myNewRandomPet = PetStore.GetRandomPet(); myNewRandomPet.Sleep(10); 

Il problema è che non sai che tipo di animale sarà. Grazie all’interfaccia, sebbene tu sappia qual è, mangia e dorme.

Quindi questa risposta potrebbe non essere stata utile, ma l’idea generale è che le interfacce ti permettono di fare cose buone come Dependency Injection e Inversion of Control dove puoi ottenere un object, avere un elenco ben definito di cose che l’object può fare senza mai VERAMENTE sapendo qual è il tipo concreto di quell’object.

La risposta più semplice è che le interfacce definiscono ciò che può fare la tua class. È un “contratto” che dice che la tua class sarà in grado di fare quell’azione.

 Public Interface IRollOver Sub RollOver() End Interface Public Class Dog Implements IRollOver Public Sub RollOver() Implements IRollOver.RollOver Console.WriteLine("Rolling Over!") End Sub End Class Public Sub Main() Dim d as New Dog() Dim ro as IRollOver = TryCast(d, IRollOver) If ro isNot Nothing Then ro.RollOver() End If End Sub 

In sostanza, si garantisce che la class Dog abbia sempre la capacità di eseguire il rollover finché continua ad implementare tale interfaccia. Se i gatti acquisiscono la capacità di RollOver (), anche loro possono implementare quell’interfaccia, e puoi trattare sia cani che gatti in modo omogeneo quando li chiedi a RollOver ().

Quando guidi l’auto di un amico, sai più o meno come farlo. Questo perché le auto convenzionali hanno tutte un’interfaccia molto simile: volante, pedali e così via. Pensa a questa interfaccia come un contratto tra costruttori di automobili e conducenti. In qualità di guidatore (l’utente / cliente dell’interfaccia in termini di software), non è necessario conoscere i dettagli di diverse auto per essere in grado di guidarli: ad esempio, tutto ciò che è necessario sapere è che la rotazione del volante rende svolta in auto. Come produttore di automobili (il fornitore di un’implementazione dell’interfaccia in termini di software) hai una chiara idea di cosa dovrebbe avere la tua nuova auto e di come dovrebbe comportarsi in modo che i guidatori possano usarli senza molto addestramento extra. Questo contratto è ciò che le persone nella progettazione del software definiscono disaccoppiamento (l’utente dal fornitore) – il codice cliente è in termini di utilizzo di un’interfaccia piuttosto che di una sua particolare implementazione e, quindi, non ha bisogno di conoscere i dettagli degli oggetti implementare l’interfaccia.

Le interfacce sono un meccanismo per ridurre l’accoppiamento tra diverse parti, eventualmente diverse di un sistema.

Da una prospettiva .NET

  • La definizione dell’interfaccia è una lista di operazioni e / o proprietà.
  • I metodi di interfaccia sono sempre pubblici.
  • L’interfaccia stessa non deve essere pubblica.

Quando si crea una class che implementa l’interfaccia, è necessario fornire un’implementazione esplicita o implicita di tutti i metodi e le proprietà definiti dall’interfaccia.

Inoltre .net ha solo una singola ereditarietà e le interfacce sono una necessità per un object di esporre i metodi ad altri oggetti che non sono a conoscenza o che si trovano al di fuori della sua gerarchia di classi. Questo è anche noto come esposizione dei comportamenti.

Un esempio un po ‘più concreto:

Si consideri che abbiamo molti DTO (oggetti di trasferimento dati) che hanno proprietà per chi è stato aggiornato per ultimo, e quando è stato. Il problema è che non tutti i DTO hanno questa proprietà perché non è sempre rilevante. Allo stesso tempo, desideriamo un meccanismo generico per garantire che queste proprietà siano impostate se disponibili quando sono inviate al stream di lavoro, ma l’object del stream di lavoro deve essere liberamente collegato dagli oggetti inviati. vale a dire che il metodo del stream di lavoro di invio non dovrebbe conoscere realmente tutte le sottigliezze di ciascun object e tutti gli oggetti nel stream di lavoro non sono necessariamente oggetti DTO.

 // first pass - not maintainable void SubmitToWorkflow(object o, User u) { if( o is StreetMap ) { var map = (StreetMap)o; map.LastUpdated = DateTime.UtcNow; map.UpdatedByUser = u.UserID; } else if( o is Person ) { var person = (Person)o; person.LastUpdated = DateTime.Now; // whoops .. should be UtcNow person.UpdatedByUser = u.UserID; } // whoa - very unmaintainable. 

Nel codice sopra, SubmitToWorkflow() deve conoscere ogni singolo object. Inoltre, il codice è un pasticcio con un enorme se / else / switch, viola il principio Do not Repeat Yourself (DRY) e richiede agli sviluppatori di ricordare le modifiche di copia / incolla ogni volta che un nuovo object viene aggiunto al sistema.

 // second pass - brittle void SubmitToWorkflow(object o, User u) { if( o is DTOBase ) { DTOBase dto = (DTOBase)o; dto.LastUpdated = DateTime.UtcNow; dto.UpdatedByUser = u.UserID; } 

Leggermente migliore, ma ancora fragile. Se vogliamo inviare altri tipi di oggetti, abbiamo ancora bisogno di più dichiarazioni di casi. eccetera.

 // third pass pass - also brittle void SubmitToWorkflow(DTOBase dto, User u) { dto.LastUpdated = DateTime.UtcNow; dto.UpdatedByUser = u.UserID; 

Ancora fragile, ed entrambi i metodi impongono il vincolo che tutti i DTO devono implementare questa proprietà che abbiamo indicato non era universalmente applicabile. Alcuni sviluppatori potrebbero essere tentati di scrivere metodi do-nothing, ma questo ha un cattivo odore. Non vogliamo che le classi fingano di supportare il monitoraggio degli aggiornamenti, ma non lo fanno.

Interfacce, come possono aiutare?

Se definiamo un’interfaccia molto semplice:

 public interface IUpdateTracked { DateTime LastUpdated { get; set; } int UpdatedByUser { get; set; } } 

Qualsiasi class che abbia bisogno di questo monitoraggio automatico degli aggiornamenti può implementare l’interfaccia.

 public class SomeDTO : IUpdateTracked { // IUpdateTracked implementation as well as other methods for SomeDTO } 

Il metodo del stream di lavoro può essere reso molto più generico, più piccolo e più gestibile, e continuerà a funzionare indipendentemente dal numero di classi che implementano l’interfaccia (DTO o altro) perché si occupa solo dell’interfaccia.

 void SubmitToWorkflow(object o, User u) { IUpdateTracked updateTracked = o as IUpdateTracked; if( updateTracked != null ) { updateTracked.LastUpdated = DateTime.UtcNow; updateTracked.UpdatedByUser = u.UserID; } // ... 
  • Possiamo notare la variazione void SubmitToWorkflow(IUpdateTracked updateTracked, User u) garantirebbe la sicurezza del tipo, tuttavia non sembra rilevante in queste circostanze.

In alcuni codici di produzione che utilizziamo, abbiamo la generazione del codice per creare queste classi DTO dalla definizione del database. L’unica cosa che lo sviluppatore fa è creare correttamente il nome del campo e decorare la class con l’interfaccia. Finché le proprietà sono chiamate LastUpdated e UpdatedByUser, funziona.

Forse ti stai chiedendo Cosa succede se il mio database è legacy e questo non è ansible? Devi solo scrivere un po ‘di più; un’altra grande caratteristica delle interfacce è che possono permetterti di creare un ponte tra le classi.

Nel codice seguente abbiamo un LegacyDTO fittizio, un object preesistente che ha campi con LegacyDTO simili. Sta implementando l’interfaccia IUpdateTracked per colbind le proprietà esistenti, ma con nomi diversi.

 // using an interface to bridge properties public class LegacyDTO : IUpdateTracked { public int LegacyUserID { get; set; } public DateTime LastSaved { get; set; } public int UpdatedByUser { get { return LegacyUserID; } set { LegacyUserID = value; } } public DateTime LastUpdated { get { return LastSaved; } set { LastSaved = value; } } } 

Potresti fare qualcosa di interessante , ma non ti confonde avere più proprietà? o Cosa succede se ci sono già quelle proprietà ma intendono qualcos’altro? .net ti dà la possibilità di implementare esplicitamente l’interfaccia. Ciò significa che le proprietà di IUpdateTracked saranno visibili solo quando utilizziamo un riferimento a IUpdateTracked. Nota come non ci sono modificatori pubblici sulla dichiarazione e la dichiarazione include il nome dell’interfaccia.

 // explicit implementation of an interface public class YetAnotherObject : IUpdatable { int IUpdatable.UpdatedByUser { ... } DateTime IUpdatable.LastUpdated { ... } 

Avere così tanta flessibilità per definire come la class implementa l’interfaccia offre allo sviluppatore molta libertà di separare l’object dai metodi che lo consumano. Le interfacce sono un ottimo modo per rompere l’accoppiamento.

C’è molto di più nelle interfacce di questo. Questo è solo un esempio semplificato della vita reale che utilizza un aspetto della programmazione basata sull’interfaccia.

Come accennato in precedenza, e da altri responder, è ansible creare metodi che accettano e / o restituiscono riferimenti all’interfaccia anziché un riferimento di class specifico. Se dovessi trovare i duplicati in una lista, potrei scrivere un metodo che accetta e restituisce un IList (un’interfaccia che definisce le operazioni che funzionano sugli elenchi) e non sono vincolato a una class di raccolta concreta.

 // decouples the caller and the code as both // operate only on IList, and are free to swap // out the concrete collection. public IList FindDuplicates( IList list ) { var duplicates = new List() // TODO - write some code to detect duplicate items return duplicates; } 

Avvertenza sul controllo della versione

Se si tratta di un’interfaccia pubblica, dichiari che garantisco che l’interfaccia x assomigli a questo! e una volta che hai spedito il codice e pubblicato l’interfaccia, non dovresti mai cambiarlo. Non appena il codice del consumatore inizia a fare affidamento su quell’interfaccia, non vuoi rompere il codice nel campo.

Vedi questo post Haacked per una buona discussione.

Interfacce contro classi astratte (base)

Le classi astratte possono fornire l’implementazione mentre le interfacce non possono. Le classi astratte sono in qualche modo più flessibili nell’aspetto di versione se si seguono alcune linee guida come il pattern NVPI (Interfaccia pubblica non virtuale).

Vale la pena ribadire che in .net, una class può ereditare solo da una singola class, ma una class può implementare tutte le interfacce che vuole.

Iniezione di dipendenza

Il rapido riassunto di interfacce e DI è che l’uso delle interfacce consente agli sviluppatori di scrivere codice programmato su un’interfaccia per fornire servizi. In pratica si può finire con un sacco di piccole interfacce e classi piccole, e un’idea è che le classi piccole che fanno una cosa e solo una cosa sono molto più facili da codificare e mantenere.

 class AnnualRaiseAdjuster : ISalaryAdjuster { AnnualRaiseAdjuster(IPayGradeDetermination payGradeDetermination) { ... } void AdjustSalary(Staff s) { var payGrade = payGradeDetermination.Determine(s); s.Salary = s.Salary * 1.01 + payGrade.Bonus; } } 

In breve, il vantaggio mostrato nel frammento di cui sopra è che la determinazione del grado di paga è appena iniettata nel sistema di aumento annuale. In che modo viene determinato il grado di retribuzione in realtà non importa a questa class. Durante il test, lo sviluppatore può prendere in giro i risultati della determinazione del grado di paga per garantire le funzioni di regolazione salariale desiderate. Anche i test sono veloci perché il test sta solo testando la class e non tutto il resto.

Questo non è un primer di DI, perché ci sono libri interi dedicati all’argomento; l’esempio sopra è molto semplificato.

Questo è un argomento piuttosto “lungo”, ma lascia che provi a metterlo in modo semplice.

Un’interfaccia è -come “lo chiamano” – un contratto. Ma dimenticati di quella parola.

Il modo migliore per capirli è attraverso una sorta di esempio di pseudo-codice. È così che li ho capiti molto tempo fa.

Supponi di avere un’app che elabora i messaggi. Un messaggio contiene alcune cose, come un sobject, un testo, ecc.

Quindi scrivi il tuo MessageController per leggere un database ed estrai messaggi. È molto bello fino a quando sentirai improvvisamente che anche i fax saranno implementati presto. Quindi ora dovrai leggere “Fax” ed elaborarli come messaggi!

Questo potrebbe facilmente trasformarsi in un codice Spagetti. Quindi quello che fai invece di avere un MessageController di controlli solo “Messaggi”, lo rendi in grado di lavorare con un’interfaccia chiamata IMessage (l’I è solo un uso comune, ma non richiesto).

La tua interfaccia IMessage, contiene alcuni dati di base di cui hai bisogno per assicurarti di essere in grado di elaborare il messaggio in quanto tale.

Quindi, quando crei le tue classi EMail, Fax, PhoneCall, le fai implementare l’ interfaccia chiamata IMessage .

Quindi nel tuo MessageController puoi avere un metodo chiamato così:

 private void ProcessMessage(IMessage oneMessage) { DoSomething(); } 

Se non avessi usato le interfacce, dovresti avere:

 private void ProcessEmail(Email someEmail); private void ProcessFax(Fax someFax); etc. 

Quindi, utilizzando un’interfaccia comune , ti sei assicurato che il metodo ProcessMessage sia in grado di funzionare con esso, indipendentemente dal fatto che si tratti di un fax, di un’e-mail, di una chiamata telefonica, ecc.

Perché o come ?

Perché l’interfaccia è un contratto che specifica alcune cose che devi rispettare (o attuare) per poterlo usare. Pensalo come un distintivo . Se il tuo object “Fax” non ha l’interfaccia IMessage, allora il tuo metodo ProcessMessage non sarebbe in grado di lavorare con quello, ti darà un tipo non valido, perché stai passando un Fax a un metodo che si aspetta un IMessage object.

Vedi il punto?

Pensa all’interfaccia come a un “sottoinsieme” di metodi e proprietà che avrai a disposizione, nonostante il vero tipo di object. Se l’object originale (Fax, Email, PhoneCall, ecc.) Implementa tale interfaccia, è ansible passare la sicurezza attraverso i metodi che richiedono tale interfaccia.

C’è più magia nascosta, puoi RIVOLGERE le interfacce ai loro oggetti originali:

Fax myFax = (Fax) SomeIMessageThatIReceive;

ArrayList () in .NET 1.1 aveva una bella interfaccia chiamata IList. Se hai un IList (molto “generico”) puoi trasformarlo in un ArrayList:

 ArrayList ar = (ArrayList)SomeIList; 

E ci sono migliaia di campioni là fuori in natura.

Interfacce come ISortable, IComparable, ecc., Definiscono i metodi e le proprietà che devi implementare nella tua class per ottenere tale funzionalità.

Per espandere il nostro esempio, potresti avere un elenco <> di email, fax, PhoneCall, tutti nella stessa lista, se il tipo è IMessage, ma non potresti averli tutti insieme se gli oggetti fossero semplicemente email, fax, ecc. .

Se si desidera ordinare (o enumerare ad esempio) i propri oggetti, occorrono loro per implementare l’interfaccia corrispondente. Nell’esempio .NET, se si dispone di un elenco di oggetti “Fax” e si desidera poterli ordinare utilizzando MyList.Sort (), è necessario impostare la class fax in questo modo:

 public class Fax : ISorteable { //implement the ISorteable stuff here. } 

Spero che questo ti dia un suggerimento. Altri utenti pubblicheranno probabilmente altri buoni esempi. In bocca al lupo! e Abbraccia il potere delle INterfacce.

avvertimento : non tutto è buono per le interfacce, ci sono alcuni problemi con loro, i puristi di OOP inizieranno una guerra su questo. Rimarrò da parte. Uno svantaggio di un’interfce (in .NET 2.0 almeno) è che non è ansible avere membri PRIVATI, o protetti, deve essere pubblico. Questo ha un senso, ma a volte desideri semplicemente dichiarare cose private o protette.

Oltre alle interfacce delle funzioni hanno all’interno dei linguaggi di programmazione, sono anche un potente strumento semantico quando esprimono idee progettuali ad altre persone .

Un codice base con interfacce ben progettate è improvvisamente molto più facile da discutere. “Sì, è necessario un CredentialsManager per registrare nuovi server remoti.” “Passare un PropertyMap a ThingFactory per ottenere un’istanza di lavoro.”

La capacità di affrontare una cosa complessa con una singola parola è piuttosto utile.

Le interfacce ti consentono di codificare gli oggetti in modo generico. Ad esempio, supponiamo tu abbia un metodo che invia rapporti. Ora dì che hai un nuovo requisito che arriva dove devi scrivere un nuovo rapporto. Sarebbe bello se potessi riutilizzare il metodo che avevi già scritto giusto? Le interfacce lo rendono facile:

 interface IReport { string RenderReport(); } class MyNewReport : IReport { public string RenderReport() { return "Hello World Report!"; } } class AnotherReport : IReport { public string RenderReport() { return "Another Report!"; } } //This class can process any report that implements IReport! class ReportEmailer() { public void EmailReport(IReport report) { Email(report.RenderReport()); } } class MyApp() { void Main() { //create specific "MyNewReport" report using interface IReport newReport = new MyNewReport(); //create specific "AnotherReport" report using interface IReport anotherReport = new AnotherReport(); ReportEmailer reportEmailer = new ReportEmailer(); //emailer expects interface reportEmailer.EmailReport(newReport); reportEmailer.EmailReport(anotherReport); } } 

Le interfacce sono anche la chiave del polimorfismo, uno dei “TRE PILASTRI DI OGGETTO”.

Alcune persone ci hanno toccato sopra, il polimorfismo significa semplicemente che una data class può assumere diverse “forms”. Significa, se abbiamo due classi, “Cane” e “Gatto” e entrambi implementiamo l’interfaccia “INeedFreshFoodAndWater” (hehe) – il tuo codice può fare qualcosa di simile a questo (pseudocodice):

 INeedFreshFoodAndWater[] array = new INeedFreshFoodAndWater[]; array.Add(new Dog()); array.Add(new Cat()); foreach(INeedFreshFoodAndWater item in array) { item.Feed(); item.Water(); } 

Questo è potente perché ti consente di trattare astrattamente diverse classi di oggetti e ti permette di fare cose come rendere i tuoi oggetti più liberamente accoppiati, ecc.

OK, quindi riguarda le classi astratte e le interfacce …

Concettualmente, le classi astratte sono lì per essere usate come classi base. Molto spesso essi stessi forniscono già alcune funzionalità di base e le sottoclassi devono fornire la propria implementazione dei metodi astratti (quelli sono i metodi che non sono implementati nella class base astratta).

Le interfacce sono principalmente utilizzate per disaccoppiare il codice client dai dettagli di una particolare implementazione. Inoltre, a volte la possibilità di passare all’implementazione senza modificare il codice client rende il codice client più generico.

A livello tecnico, è più difficile tracciare una linea tra le classi e le interfacce astratte, perché in alcuni linguaggi (ad esempio, C ++), non c’è alcuna differenza sintattica, o perché è ansible utilizzare anche classi astratte ai fini del disaccoppiamento o della generalizzazione. Usare una class astratta come interfaccia è ansible perché ogni class base, per definizione, definisce un’interfaccia che tutte le sue sottoclassi dovrebbero onorare (cioè, dovrebbe essere ansible usare una sottoclass invece di una class base).

Le interfacce sono un modo per far rispettare un object che implementa una certa quantità di funzionalità, senza dover utilizzare l’ereditarietà (che porta a un codice fortemente accoppiato, anziché accoppiato liberamente che può essere ottenuto attraverso l’uso di interfacce).

Le interfacce descrivono la funzionalità, non l’implementazione.

In poche parole: un’interfaccia è una class definita dai metodi ma non implementata in essi. Al contrario, una class astratta ha alcuni dei metodi implementati, ma non tutti.

Pensa a un’interfaccia come a un contratto. Quando una class implementa un’interfaccia, accetta essenzialmente di rispettare i termini di quel contratto. Come consumatore, ti interessa solo che gli oggetti che hai possano svolgere i loro doveri contrattuali. I loro meccanismi e dettagli interni non sono importanti.

Una buona ragione per utilizzare un’interfaccia rispetto a una class astratta in Java è che una sottoclass non può estendere più classi base, ma può implementare più interfacce.

Java non consente l’ereditarietà multipla (per ottime ragioni, cerca un diamante spaventoso) ma cosa succede se vuoi che la tua class fornisca diverse serie di comportamenti? Dite che volete che chiunque lo usi sappia che può essere serializzato, e che può anche dipingersi sullo schermo. la risposta è di implementare due interfacce diverse.

Poiché le interfacce non contengono alcuna implementazione propria e nessun membro di istanza, è sicuro implementarne diversi nella stessa class senza ambiguità.

Il lato negativo è che dovrete avere l’implementazione in ogni class separatamente. Quindi, se la tua gerarchia è semplice e ci sono parti dell’implementazione che dovrebbero essere uguali per tutte le classi ereditanti, usa una class astratta.

Come altri hanno detto qui, le interfacce definiscono un contratto (come le classi che usano l’interfaccia “guarderanno”) e le classi astratte definiscono la funzionalità condivisa.

Vediamo se il codice aiuta:

 public interface IReport { void RenderReport(); // this just defines the method prototype } public abstract class Reporter { protected void DoSomething() { // this method is the same for every class that inherits from this class } } public class ReportViolators : Reporter, IReport { public void RenderReport() { // some kind of implementation specific to this class } } public class ClientApp { var violatorsReport = new ReportViolators(); // the interface method violatorsReport.RenderReport(); // the abstract class method violatorsReport.DoSomething(); } 

Supponendo che tu ti stia riferendo alle interfacce in linguaggi orientati agli oggetti tipicamente staticamente, l’uso principale è nell’affermare che la tua class segue un particolare contratto o protocollo.

Di ‘che hai:

 public interface ICommand { void Execute(); } public class PrintSomething : ICommand { OutputStream Stream { get; set; } String Content {get; set;} void Execute() { Stream.Write(content); } } 

Ora hai una struttura di comando sostituibile. Qualsiasi istanza di una class che implementa IExecute può essere archiviata in un elenco di qualche tipo, dire qualcosa che implementa IEnumerable e puoi scorrere ciclicamente ed eseguire ognuno di essi, sapendo che ogni object farà solo la cosa giusta. È ansible creare un comando composito implementando CompositeCommand che avrà il proprio elenco di comandi da eseguire o un LoopingCommand per eseguire ripetutamente un insieme di comandi, quindi si avrà la maggior parte di un semplice interprete.

Quando puoi ridurre un insieme di oggetti a un comportamento che tutti hanno in comune, potresti avere la causa di estrarre un’interfaccia. Inoltre, a volte è ansible utilizzare le interfacce per impedire agli oggetti di intromettersi accidentalmente sulle preoccupazioni di quella class; ad esempio, è ansible implementare un’interfaccia che consente solo ai client di recuperare, anziché modificare i dati nell’object, e che la maggior parte degli oggetti riceva solo un riferimento all’interfaccia di recupero.

Le interfacce funzionano meglio quando le interfacce sono relativamente semplici e fanno alcune ipotesi.

Cercare il principio di sussistenza Liskov per dare più senso a questo.

Alcuni linguaggi tipizzati in modo statico come il C ++ non supportano le interfacce come un concetto di prima class, quindi crei interfacce usando classi astratte pure.

Aggiornamento Poiché sembra che tu stia chiedendo delle classi astratte e delle interfacce, ecco la mia semplificazione eccessiva preferita:

  • Le interfacce definiscono funzionalità e funzionalità.
  • Le classi astratte definiscono le funzionalità principali.

In genere, eseguo un refactoring dell’interfaccia di estrazione prima di creare una class astratta. Sono più propenso a creare una class astratta se penso che dovrebbe esserci un contratto di creazione (in particolare, che un tipo specifico di costruttore dovrebbe essere sempre supportato da sottoclassi). Tuttavia, raramente uso classi astratte “pure” in C # / java. Ho molta più probabilità di implementare una class con almeno un metodo che contiene un comportamento significativo e di usare metodi astratti per supportare i metodi di template chiamati da quel metodo. Quindi la class astratta è un’implementazione base di un comportamento, che tutte le sottoclassi concrete possono sfruttare senza dover reimplementare.

Risposta semplice: un’interfaccia è un insieme di firme del metodo (+ tipo di ritorno). Quando un object dice che implementa un’interfaccia, sai che espone quell’insieme di metodi.

Le interfacce sono un modo per implementare le convenzioni in un modo che è ancora fortemente tipizzato e polimorfico.

Un buon esempio del mondo reale sarebbe IDisposable in .NET. Una class che implementa l’interfaccia IDisposable forza tale class a implementare il metodo Dispose (). Se la class non implementa Dispose (), si verificherà un errore del compilatore durante il tentativo di creare. Inoltre, questo modello di codice:

 using (DisposableClass myClass = new DisposableClass()) { // code goes here } 

Farà sì che myClass.Dispose () venga eseguito automaticamente quando l’esecuzione esce dal blocco interno.

Tuttavia, e questo è importante, non vi è alcuna applicazione su cosa dovrebbe fare il metodo Dispose (). Potresti fare in modo che il tuo metodo Dispose () scelga ricette casuali da un file e le invii via email a una lista di distribuzione, al compilatore non interessa. L’intento del modello IDisposable è rendere più facile la pulizia delle risorse. Se le istanze di una class si manterranno su handle di file, IDisposable semplifica molto la centralizzazione del deallocation e del codice di cleanup in un punto e per promuovere uno stile di utilizzo che garantisca sempre la deallocazione.

E questa è la chiave per le interfacce. Sono un modo per ottimizzare convenzioni di programmazione e modelli di progettazione. Che, se usato correttamente, promuove un codice più semplice e autodocumentante che è più facile da usare, più facile da mantenere e più corretto.

Ecco un esempio di db che uso spesso. Supponiamo che tu abbia un object e un object contenitore come un elenco. Supponiamo che qualche volta potresti voler memorizzare gli oggetti in una particolare sequenza. Supponiamo che la sequenza non sia correlata alla posizione nell’array ma che gli oggetti siano un sottoinsieme di un set di oggetti più grande e che la posizione della sequenza sia correlata al filtro sql del database.

Per tenere traccia delle posizioni di sequenza personalizzate, è ansible rendere l’object implementare un’interfaccia personalizzata. L’interfaccia personalizzata potrebbe mediare lo sforzo organizzativo richiesto per mantenere tali sequenze.

Ad esempio, la sequenza che ti interessa non ha nulla a che fare con le chiavi primarie nei record. Con l’object che implementa l’interfaccia si potrebbe dire myObject.next () o myObject.prev ().

Ho avuto lo stesso problema di te e trovo la spiegazione del “contratto” un po ‘confusa.

Se si specifica che un metodo utilizza un’interfaccia IEnumerable come parametro in-end, si può affermare che si tratta di un contratto che specifica che il parametro deve essere di un tipo che eredita dall’interfaccia IEnumerable e quindi supporta tutti i metodi specificati nell’interfaccia IEnumerable. Lo stesso sarebbe vero anche se usassimo una class astratta o una class normale. Qualsiasi object che erediti da quelle classi sarebbe ok da passare come parametro. In ogni caso, si potrebbe dire che l’object ereditato supporta tutti i metodi pubblici nella class base se la class base è una class normale, una class astratta o un’interfaccia.

Una class astratta con tutti i metodi astratti è fondamentalmente la stessa di un’interfaccia, quindi potresti dire che un’interfaccia è semplicemente una class senza metodi implementati. Si potrebbero effettivamente rilasciare interfacce dal linguaggio e utilizzare invece la class astratta con solo metodi astratti. Penso che la ragione per cui li separiamo sia per ragioni semantiche, ma per ragioni di codifica non vedo la ragione e la trovo semplicemente confusa.

Un altro suggerimento potrebbe essere quello di rinominare l’interfaccia per la class di interfaccia in quanto l’interfaccia è solo un’altra variante di una class.

In alcune lingue ci sono sottili differenze che permettono ad una class di ereditare solo 1 class ma più interfacce mentre in altre si potrebbero avere molte di entrambe, ma questo è un altro problema e non direttamente correlato

Il modo più semplice per comprendere le interfacce è iniziare considerando che cosa significa l’ereditarietà della class. Comprende due aspetti:

  1. I membri di una class derivata possono utilizzare come membri i membri pubblici o protetti di una class base.
  2. I membri di una class derivata possono essere usati dal codice che si aspetta un membro della class base (nel senso che sono sostituibili).

Both of these features are useful, but because it is difficult to allow a class to use members of more than one class as its own, many languages and frameworks only allow classs to inherit from a single base class. On the other hand, there is no particular difficulty with having a class be substitutable for multiple other unrelated things.

Further, because the first benefit of inheritance can be largely achieved via encapsulation, the relative benefit from allowing multiple-inheritance of the first type is somewhat limited. On the other hand, being able to substitute an object for multiple unrelated types of things is a useful ability which cannot be readily achieved without language support.

Interfaces provide a means by which a language/framework can allow programs to benefit from the second aspect of inheritance for multiple base types, without requiring it to also provide the first.

Most of the interfaces you come across are a collection of method and property signatures. Any one who implements an interface must provide definitions to what ever is in the interface.

Interface is like a fully abstract class. That is, an abstract class with only abstract members. You can also implement multiple interfaces, it’s like inheriting from multiple fully abstract classs. Anyway.. this explanation only helps if you understand what an abstract class is.

Moved to this question after this question was closed as a duplicate, in hope that it will help someone:

In your simple case, you could achieve something similar to what you get with interfaces by using a common base class that implements show() (or perhaps defines it as abstract). Let me change your generic names to something more concrete, Eagle and Hawk instead of MyClass1 and MyClass2 . In that case you could write code like

 Bird bird = GetMeAnInstanceOfABird(someCriteriaForSelectingASpecificKindOfBird); bird.Fly(Direction.South, Speed.CruisingSpeed); 

That lets you write code that can handle anything that is a Bird . You could then write code that causes the Bird to do it’s thing (fly, eat, lay eggs, and so forth) that acts on an instance it treats as a Bird . That code would work whether Bird is really an Eagle , Hawk , or anything else that derives from Bird .

That paradigm starts to get messy, though, when you don’t have a true is a relationship. Say you want to write code that flies things around in the sky. If you write that code to accept a Bird base class, it suddenly becomes hard to evolve that code to work on a JumboJet instance, because while a Bird and a JumboJet can certainly both fly, a JumboJet is most certainly not a Bird .

Enter the interface.

What Bird (and Eagle , and Hawk ) do have in common is that they can all fly. If you write the above code instead to act on an interface, IFly , that code can be applied to anything that provides an implementation to that interface.

Interfaces require any class that implements them to contain the methods defined in the interface.

The purpose is so that, without having to see the code in a class, you can know if it can be used for a certain task. For example, the Integer class in Java implements the comparable interface, so, if you only saw the method header (public class String implements Comparable), you would know that it contains a compareTo() method.