Come faccio a sapere quando creare un'interfaccia?


196

Sono a un certo punto del mio sviluppo e sto imparando dove sento di dover imparare di più sulle interfacce.

Leggo spesso su di loro, ma sembra proprio che non riesca a coglierli.

Ho letto esempi come: Animal base class, con interfaccia IAnimal per cose come 'Walk', 'Run', 'GetLegs', ecc. - Ma non ho mai lavorato su qualcosa e mi sono sentito come "Ehi, dovrei usare un'interfaccia Qui!"

Cosa mi sto perdendo? Perché è un concetto così difficile da afferrare! Sono solo intimidito dal fatto che potrei non rendermi mai conto di una necessità concreta per uno, principalmente a causa di un aspetto mancante della loro comprensione! Mi fa sentire che mi manca qualcosa in alto in termini di essere uno sviluppatore! Se qualcuno ha avuto un'esperienza come questa e ha avuto una svolta, apprezzerei alcuni consigli su come comprendere questo concetto. Grazie.


1
È necessario verificare stackoverflow.com/q/8531292/1055241
gprathour

Risposte:


151

risolve questo problema concreto:

hai a, b, c, d di 4 tipi diversi. in tutto il codice hai qualcosa come:

a.Process();
b.Process();
c.Process();
d.Process();

perché non li hanno implementare IProcessable e quindi farlo

List<IProcessable> list;

foreach(IProcessable p in list)
    p.Process();

questo ridimensionerà molto meglio quando aggiungi, diciamo, 50 tipi di classi che fanno tutte la stessa cosa.


Un altro problema concreto:

Hai mai dato un'occhiata a System.Linq.Enumerable? Definisce una tonnellata di metodi di estensione che operano su qualsiasi tipo che implementa IEnumerable. Poiché tutto ciò che implementa IEnumerable in pratica dice "I iteration in un modello di tipo foreach non ordinato", è possibile definire comportamenti complessi (Count, Max, Where, Select, ecc.) Per qualsiasi tipo enumerabile.


2
Questo aiuta. Qual è il vantaggio di avere un'interfaccia presente piuttosto che avere i tipi che tutti hanno l'implementazione per il metodo Process ()?
user53885

Non saresti in grado di usare la stessa variabile p se non fossero tutte sottoclassi dello stesso tipo di base o non implementassero l'interfaccia.
Karl,

Non è necessario eseguire il cast, invece di 50 classi diverse con un metodo Process. C # non usa la "tipizzazione anatra", quindi solo perché A ha Process () e B ha Process () non significa che ci sia un modo generico per chiamare. Hai bisogno di un'interfaccia per questo.
user7116

giusto. Ho appena cambiato "var" in "IProcessable" per dare più senso all'esempio.
Jimmy,

2
@Rogerio: stavo cercando di essere generico. Il punto non è che "quando hai cose che hanno una funzione Process ()" è "quando hai cose che condividono un insieme comune di metodi". l'esempio può essere facilmente modificato inforeach(IMyCompanyWidgetFrobber a in list) a.Frob(widget, context);
Jimmy

133

Mi piace molto la risposta di Jimmy, ma sento di dover aggiungere qualcosa. La chiave di tutto è la "capacità" in IProcess grado di. Indica una capacità (o proprietà, ma significa "qualità intrinseca", non nel senso delle proprietà C #) dell'oggetto che implementa l'interfaccia. IAnimal non è probabilmente un buon esempio per un'interfaccia, ma IWalkable potrebbe essere una buona interfaccia da avere se il tuo sistema ha molte cose che possono camminare. Potresti avere classi derivate da animali come cane, mucca, pesce, serpente. I primi due probabilmente implementerebbero IWalkable, gli ultimi due non camminano, quindi non lo farebbero. Ora chiedi "perché non avere un'altra superclasse, WalkingAnimal, da cui derivano Dog e Cow?". La risposta è quando hai qualcosa di completamente fuori dall'albero dell'eredità che può anche camminare, come un robot. Il robot implementerebbe IWalkable, ma probabilmente non deriverebbe da Animal. Se vuoi un elenco di cose che possono camminare,

Ora sostituisci IWalkable con qualcosa di più software come IPersistable e l'analogia diventa molto più vicina a ciò che vedresti in un vero programma.


9
Mi piace quello che hai inventato: "Indica una capacità". Le interfacce sono davvero necessarie quando devi definire la capacità perché le basi devono attenersi alla sua classe "Base".
Ramiz Uddin

71

Utilizzare le interfacce quando le implementazioni della stessa funzionalità differiranno.

Utilizzare una classe astratta / base quando è necessario condividere un'implementazione concreta comune.


8
Il primo si chiama polimorfismo. Il secondo è l'olio di serpenti - a meno che la sottoclasse non sia di base (non vi è alcuna violazione del principio di sostituzione di Liskov), dovresti favorire la composizione rispetto all'eredità.
Arnis Lapsa,

@ArnisLapsa Non capisco bene cosa intendi con "a meno che la sottoclasse non sia una baseclass". Quando una sottoclasse non "sarebbe" baseclass? (come nella isparola chiave)
Marc.2377,

32

Pensa a un'interfaccia come a un contratto. È un modo per dire "Queste classi dovrebbero seguire questo insieme di regole".

Quindi, nell'esempio IAnimal, è un modo per dire "DEVO essere in grado di chiamare Run, Walk, ecc. Su classi che implementano IAnimal".

Perché è utile? Potresti voler creare una funzione che si basa sul fatto che devi essere in grado di chiamare Run and Walk, ad esempio, sull'oggetto. Potresti avere quanto segue:

public void RunThenWalk(Monkey m) {
    m.Run();
    m.Walk();
}

public void RunThenWalk(Dog d) {
    d.Run();
    d.Walk();
}

... e ripeterlo per tutti gli oggetti che conosci possono correre e camminare. Tuttavia, con la tua interfaccia IAnimal, puoi definire la funzione una volta come segue:

public void RunThenWalk(IAnimal a) {
    a.Run();
    a.Walk();
}

Programmando contro l'interfaccia, in sostanza ti fidi delle classi per implementare l'intento dell'interfaccia. Quindi, nel nostro esempio, il pensiero è "Non mi interessa come corrono e camminano, purché corrano e camminano. Il mio RunThenWalk sarà valido fino a quando raggiungeranno tale accordo. Funziona perfettamente senza sapere altro la classe."

C'è anche una buona discussione in questa domanda correlata .


1
Non dimenticare l'indipendenza dall'implementazione. Un'interfaccia consente di non preoccuparsi di come viene implementata la funzione sottostante, solo che fa ciò che l'interfaccia dice.
Matthew Brubaker,

18

Non preoccuparti così tanto. Molti sviluppatori dovranno raramente scrivere un'interfaccia. Utilizzerai spesso le interfacce disponibili all'interno del framework .NET , ma se non senti la necessità di scriverne una in qualunque momento presto non c'è nulla di sorprendente in questo.

L'esempio che do sempre a qualcuno è se hai una classe di barche a vela e una classe Viper. Ereditano rispettivamente la classe Boat e la classe Car. Ora dì che devi scorrere tutti questi oggetti e chiamarne il Drive()metodo. Mentre potresti scrivere del codice come il seguente:

if(myObject is Boat)
    ((Boat)myObject).Drive()
else
    if (myObject is Car)
        ((Car)myObject).Drive()

Sarebbe molto più semplice scrivere:

((IDrivable)myObject).Drive()

16

Jimmy ha ragione, quando vuoi essere in grado di usare una singola variabile per più tipi, ma tutti questi tipi implementano lo stesso metodo tramite una dichiarazione di interfaccia. Quindi puoi chiamarli metodo principale sulla variabile digitata dall'interfaccia.

C'è un secondo motivo per usare le interfacce, comunque. Quando l'architetto del progetto è una persona diversa rispetto al programmatore di implementazione, oppure esistono diversi programmatori di implementazione e un project manager. Il responsabile può scrivere un sacco di interfacce e vedere che il sistema interagisce, quindi lasciare agli sviluppatori il compito di compilare le interfacce con le classi di implementazione. Questo è il modo migliore per garantire a più persone di scrivere classi compatibili e possono farlo in parallelo.


15

Mi piace l'analogia dell'esercito.

Al sergente non importa se sei uno sviluppatore di software , un musicista o un avvocato .
Sei trattato come un soldato .

UML

È più facile per il sergente non preoccuparsi dei dettagli specifici delle persone con cui lavora,
trattare tutti come astrazioni di soldati (... e punirli quando non riescono a comportarsi come quelli).

La capacità delle persone di agire come soldati si chiama polimorfismo.

Le interfacce sono costruzioni software che aiutano a raggiungere il polimorfismo.

Necessità di dettagli astratti per ottenere semplicità è la risposta alla tua domanda.

Il polimorfismo , che etimologicamente significa "molte forme", è la capacità di trattare un oggetto di qualsiasi sottoclasse di una classe base come se fosse un oggetto della classe base. Una classe base ha quindi molte forme: la classe base stessa e una delle sue sottoclassi.

(..) Ciò semplifica la scrittura del codice e la comprensione da parte degli altri. Inoltre rende il tuo codice estensibile, perché altre sottoclassi potrebbero essere aggiunte in seguito alla famiglia di tipi e anche gli oggetti di quelle nuove sottoclassi funzionerebbero con il codice esistente.


14

Nella mia esperienza, la forza trainante per creare interfacce non si è verificata fino a quando non ho iniziato a fare test unitari con un framework beffardo. È diventato abbondantemente chiaro che l'uso delle interfacce avrebbe reso molto più semplice il derisione (poiché il framework dipendeva dal fatto che i metodi fossero virtuali). Una volta iniziato, ho visto il valore di sottrarre l'interfaccia alla mia classe dall'implementazione. Anche se non creo un'interfaccia reale, provo ora a rendere virtuali i miei metodi (fornendo un'interfaccia implicita che può essere ignorata).

Ci sono molte altre ragioni che ho scoperto per rafforzare la buona pratica del refactoring delle interfacce, ma la cosa test / derisione dell'unità era ciò che ha fornito il "momento aha" iniziale dell'esperienza pratica.

EDIT : Per chiarire, con unit testing e beffardo ho sempre due implementazioni: l'implementazione reale, concreta e un'implementazione simulata alternativa utilizzata nei test. Una volta che hai due implementazioni, il valore dell'interfaccia diventa evidente: gestiscilo in termini di interfaccia in modo da poter sostituire l'implementazione in qualsiasi momento. In questo caso lo sto sostituendo con un'interfaccia finta. So che posso farlo senza un'interfaccia reale se la mia classe è costruita correttamente, ma l'uso di un'interfaccia reale lo rinforza e lo rende più pulito (più chiaro per il lettore). Senza questo slancio, non credo che avrei apprezzato il valore delle interfacce poiché solo la maggior parte delle mie classi ha mai avuto un'unica implementazione concreta.


Cosa giusta per ragioni sbagliate. Nel tuo caso - finisci con le cosiddette "interfacce di intestazione" e la complessità aggiunta sovrappeso raggiunto semplicità.
Arnis Lapsa,

@Arnis - il test unitario è una "ragione sbagliata", deridere facilmente le classi per rimuovere le dipendenze nei test è una "ragione sbagliata". Scusa, ma non sono d'accordo.
tvanfosson,

1
i test dovrebbero influenzare il design indirettamente fornendo feedback se il codice è o non è testabile. aggiungere punti di estensibilità per migliorare la testabilità stessa è come imbrogliare. Penso che Mark Seeman lo sommi meglio bit.ly/esi8Wp
Arnis Lapsa

1
@Arnis - quindi usi le beffe nei tuoi test unitari? In caso contrario, come rimuovere la dipendenza dalle dipendenze. Stai usando DI a tutti? I test unitari mi hanno spinto a usare derisione e DI; beffardo e DI hanno dimostrato il valore della buona pratica di usare le interfacce per definire i contratti in un modo che nessuna comprensione accademica avrebbe mai potuto. A causa della mia adozione di TDD, il mio codice è molto meno accoppiato di quanto non sarebbe stato altrimenti. Penso che sia una buona cosa.
tvanfosson,

solo dicendo che la decomposizione che non è lungo le cosiddette articolazioni naturali porta a bassa coesione e complessità inutile.
Arnis Lapsa,

10

Alcuni esempi non di programmazione che potrebbero aiutarti a vedere gli usi appropriati delle interfacce nella programmazione.

C'è un'interfaccia tra i dispositivi elettrici e la rete elettrica: è l' insieme di convenzioni sulla forma delle spine e delle prese e sulle tensioni / correnti che li attraversano. Se si desidera implementare un nuovo dispositivo elettrico, purché la spina segua le regole, sarà in grado di ottenere servizi dalla rete. Ciò rende l' estensibilità molto semplice e rimuove o riduce i costi di coordinamento : non è necessario informare il fornitore di energia elettrica su come funziona il nuovo dispositivo e raggiungere un accordo separato su come collegare il nuovo dispositivo alla rete.

I paesi hanno spessori ferroviari standard. Ciò consente una divisione del lavoro tra le società di ingegneria che abbattono le rotaie e le società di ingegneria che costruiscono treni per correre su quelle rotaie e consente alle compagnie ferroviarie di sostituire e aggiornare i treni senza effettuare ricerche sull'intero sistema.

Il servizio che un'azienda presenta ad un cliente può essere descritto come un'interfaccia: un'interfaccia ben definita enfatizza il servizio e nasconde i mezzi . Quando metti una lettera in una casella di posta, ti aspetti che il sistema postale consegni la lettera entro un determinato tempo, ma non hai aspettative su come la lettera viene recapitata: non è necessario che tu sappia , e il servizio postale ha la flessibilità di scegliere i mezzi di consegna che meglio soddisfano i requisiti e le circostanze attuali. Un'eccezione a ciò è la capacità dei clienti di scegliere la posta aerea: non è il tipo di interfaccia che un moderno programmatore di computer avrebbe progettato, poiché rivela troppa implementazione.

Esempi dalla natura: non mi piacciono troppo gli esempi di eats (), makesSound (), move (), ecc. Descrivono il comportamento, che è corretto, ma non descrivono le interazioni e il modo in cui sono abilitate . Gli esempi ovvi di interfacce che consentono interazioni in natura hanno a che fare con la riproduzione, ad esempio un fiore fornisce una certa interfaccia a un'ape in modo che possa avvenire l'impollinazione.


5

È del tutto possibile andare avanti per tutta la vita come sviluppatore .net e non scrivere mai le tue interfacce. Dopotutto, siamo sopravvissuti bene senza di loro per decenni e le nostre lingue erano ancora complete di Turing.

Non posso dirti perché hai bisogno di interfacce, ma posso darti un elenco di dove li usiamo nel nostro progetto attuale:

  1. Nel nostro modello di plug-in, cariciamo i plug-in per interfaccia e forniamo tale interfaccia a chi scrive i plug-in a cui conformarsi.

  2. Nel nostro sistema di messaggistica intermachine, le classi di messaggi implementano tutte un'interfaccia specifica e vengono "scartate" utilizzando l'interfaccia.

  3. Il nostro sistema di gestione della configurazione definisce un'interfaccia utilizzata per impostare e recuperare le impostazioni di configurazione.

  4. Abbiamo un'interfaccia che usiamo per evitare un brutto problema di riferimento circolare. (Non farlo se non è necessario.)

Immagino che se c'è una regola, è usare le interfacce quando vuoi raggruppare diverse classi all'interno di una relazione is-a, ma non vuoi fornire alcuna implementazione nella classe base.


5

Un esempio di codice (combinazione di Andrew's con un mio extra in quello che è lo scopo delle interfacce ), che fornisce anche un esempio del perché l'interfaccia anziché una classe astratta sui linguaggi senza supporto per l'ereditarietà multipla (c # e Giava):

interface ILogger
{
    void Log();
}
class FileLogger : ILogger
{
    public void Log() { }
}
class DataBaseLogger : ILogger
{
    public void Log() { }
}
public class MySpecialLogger : SpecialLoggerBase, ILogger
{
    public void Log() { }
}

Si noti che FileLogger e DataBaseLogger non necessitano dell'interfaccia (potrebbe essere una classe base astratta Logger). Ma considera che ti viene richiesto di usare un logger di terze parti che ti costringe a usare una classe base (diciamo che espone metodi protetti che devi usare). Poiché il linguaggio non supporta l'ereditarietà multipla, non sarà possibile utilizzare l'approccio di classe base astratto.

La linea di fondo è: utilizzare un'interfaccia quando possibile per ottenere una maggiore flessibilità sul codice. L'implementazione è meno vincolata, quindi si adatta meglio al cambiamento.


4

Di tanto in tanto ho usato le interfacce ed ecco il mio ultimo utilizzo (i nomi sono stati generalizzati):

Ho un sacco di controlli personalizzati su un WinForm che devono salvare i dati sul mio oggetto business. Un approccio consiste nel chiamare ciascun controllo separatamente:

myBusinessObject.Save(controlA.Data);
myBusinessObject.Save(controlB.Data);
myBusinessObject.Save(controlC.Data);

Il problema con questa implementazione è che ogni volta che aggiungo un controllo devo andare nel mio metodo "Salva dati" e aggiungere il nuovo controllo.

Ho modificato i miei controlli per implementare un'interfaccia ISaveable che ha un metodo SaveToBusinessObject (...), quindi ora il mio metodo "Salva dati" scorre solo i controlli e se ne trova uno che è ISaveable, chiama SaveToBusinessObject. Quindi ora quando è necessario un nuovo controllo, tutto quello che qualcuno deve fare è implementare ISaveable in quell'oggetto (e non toccare mai un'altra classe).

foreach(Control c in Controls)
{
  ISaveable s = c as ISaveable;

  if( s != null )
      s.SaveToBusinessObject(myBusinessObject);
}

Spesso il vantaggio non realizzato per le interfacce è la localizzazione delle modifiche. Una volta definito, raramente modificherai il flusso complessivo di un'applicazione ma spesso effettuerai modifiche a livello di dettaglio. Quando si mantengono i dettagli in oggetti specifici, una modifica in ProcessA non influirà su una modifica in ProcessB. (Anche le classi di base ti danno questo vantaggio.)

EDIT: un altro vantaggio è la specificità nelle azioni. Come nel mio esempio, tutto ciò che voglio fare è salvare i dati; Non mi interessa che tipo di controllo sia o se possa fare altro: voglio solo sapere se posso salvare i dati nel controllo. Rende il mio codice di salvataggio abbastanza chiaro - non ci sono controlli per vedere se è testo, numerico, booleano o altro perché il controllo personalizzato gestisce tutto ciò.


4

Dovresti definire un'interfaccia una volta che devi forzare un comportamento per la tua classe.

Il comportamento di un animale può comportare camminare, mangiare, correre, ecc. Pertanto, si definiscono come interfacce.

Un altro esempio pratico è l'interfaccia ActionListener (o Runnable). Li implementeresti quando devi tenere traccia di un particolare evento. Pertanto, è necessario fornire l'implementazione per il actionPerformed(Event e)metodo nella propria classe (o sottoclasse). Allo stesso modo, per l'interfaccia Runnable, si fornisce l'implementazione per il public void run()metodo.

Inoltre, puoi avere queste interfacce implementate da qualsiasi numero di classi.

Un'altra istanza in cui vengono utilizzate le interfacce (in Java) è l'implementazione dell'ereditarietà multipla offerta in C ++.


3
Per favore, Dio li faccia smettere di dire cose come eredità multipla riguardo alle interfacce. È NON eredita un'interfaccia in una classe. Lo implementi .
Andrei Rînea,

4

Supponiamo che tu voglia voler modellare i fastidi che potrebbero verificarsi quando provi ad andare a dormire.

Modello prima delle interfacce

inserisci qui la descrizione dell'immagine

class Mosquito {
    void flyAroundYourHead(){}
}

class Neighbour{
    void startScreaming(){}
}

class LampJustOutsideYourWindow(){
    void shineJustThroughYourWindow() {}
}

Come vedi chiaramente molte "cose" possono essere fastidiose quando provi a dormire.

Utilizzo di classi senza interfacce

Ma quando si tratta di usare queste classi abbiamo un problema. Non hanno nulla in comune. Devi chiamare ciascun metodo separatamente.

class TestAnnoyingThings{
    void testAnnoyingThinks(Mosquito mosquito, Neighbour neighbour, LampJustOutsideYourWindow lamp){
         if(mosquito != null){
             mosquito.flyAroundYourHead();
         }
         if(neighbour!= null){
             neighbour.startScreaming();
         }
         if(lamp!= null){
             lamp.shineJustThroughYourWindow();
         }
    }
}

Modello con interfacce

Per ovviare a questo problema possiamo introdurre un iterfaceinserisci qui la descrizione dell'immagine

interface Annoying{
   public void annoy();

}

E implementalo all'interno delle classi

class Mosquito implements Annoying {
    void flyAroundYourHead(){}

    void annoy(){
        flyAroundYourHead();
    }
}

class Neighbour implements Annoying{
    void startScreaming(){}

    void annoy(){
        startScreaming();
    }
}

class LampJustOutsideYourWindow implements Annoying{
    void shineJustThroughYourWindow() {}

    void annoy(){
        shineJustThroughYourWindow();
    }
}

Utilizzo con interfacce

Ciò renderà l'utilizzo di queste classi molto più semplice

class TestAnnoyingThings{
    void testAnnoyingThinks(Annoying annoying){
        annoying.annoy();
    }
}

Ok, ma non lo è Neighboure LampJustOutsideYourWindowbisogna anche implementarlo Annoying?
Stardust,

Sì, grazie per averlo sottolineato. Ho apportato una modifica con questa modifica
Marcin Szymczak,

2

L'esempio più semplice da dare è qualcosa come i Processori di pagamento (Paypal, PDS ecc.).

Supponi di creare un'interfaccia IPaymentProcessor con i metodi ProcessACH e ProcessCreditCard.

Ora puoi implementare un'implementazione concreta di Paypal. Fare in modo che questi metodi chiamino le funzioni specifiche di PayPal.

Se decidi in seguito, devi passare a un altro fornitore, puoi farlo. Basta creare un'altra implementazione concreta per il nuovo provider. Poiché tutto ciò a cui sei legato è la tua interfaccia (contratto), puoi scambiare quale utilizza la tua applicazione senza cambiare il codice che la consuma.


2

Consente inoltre di eseguire test di unità Mock (.Net). Se la tua classe utilizza un'interfaccia, puoi deridere l'oggetto nel test dell'unità e testare facilmente la logica (senza effettivamente colpire il database, il servizio web, ecc.).

http://www.nmock.org/


2

Se si sfogliano gli assembly .NET Framework e si esegue il drill-down nelle classi di base per uno qualsiasi degli oggetti standard, si noteranno molte interfacce (membri denominati come ISomeName).

Le interfacce sono fondamentalmente per l'implementazione di framework, grandi o piccoli. Mi sono sentito allo stesso modo riguardo alle interfacce fino a quando non ho voluto scrivere un framework tutto mio. Ho anche scoperto che la comprensione delle interfacce mi ha aiutato a imparare i framework molto più rapidamente. Nel momento in cui desideri scrivere una soluzione più elegante per qualsiasi cosa, scoprirai che un'interfaccia ha molto senso. È come un metodo per permettere a una classe di indossare gli abiti appropriati per il lavoro. Ancora più importante, le interfacce consentono ai sistemi di diventare molto più autocompattanti, poiché gli oggetti complessi diventano meno complessi quando la classe implementa le interfacce, il che aiuta a classificare le sue funzionalità.

Le classi implementano interfacce quando vogliono essere in grado di partecipare in modo esplicito o implicito a un framework. Ad esempio, IDisposable è un'interfaccia comune che fornisce la firma del metodo per il metodo Dispose () popolare e utile. In un framework, tutto ciò che tu o un altro sviluppatore dovete sapere su una classe è che se implementa IDisposable, allora sapete che ((IDisposable) myObject) .Dispose () è disponibile per essere chiamato per scopi di pulizia.

ESEMPIO CLASSICO: senza implementare l'interfaccia IDisposable, non è possibile utilizzare il costrutto della parola chiave "using ()" in C #, poiché richiede che qualsiasi oggetto specificato come parametro possa essere implicitamente trasmesso su IDisposable.

ESEMPIO COMPLESSO: Un esempio più complesso potrebbe essere la classe System.ComponentModel.Component. Questa classe implementa sia IDisposable che IComponent. La maggior parte, se non tutti, gli oggetti .NET a cui è associato un designer visivo implementano IComponent in modo che l'IDE sia in grado di interagire con il componente.

CONCLUSIONE: man mano che acquisisci familiarità con .NET Framework, la prima cosa che farai quando incontrerai una nuova classe nel Browser degli oggetti o nello strumento .NET Reflector (gratuito) ( http://www.red-gate.com / products / reflector / ) è per verificare da quale classe eredita e anche le interfacce che implementa. .NET Reflector è persino migliore di Object Browser perché ti consente di vedere anche le classi derivate. Ciò ti consente di conoscere tutti gli oggetti che derivano da una determinata classe, quindi potenzialmente conoscere le funzionalità del framework che non sapevi esistessero. Ciò è particolarmente significativo quando vengono aggiunti nuovi spazi dei nomi aggiornati o nuovi a .NET Framework.


2

Considera di creare un gioco di tiro in prima persona. Il giocatore ha più pistole tra cui scegliere.

Possiamo avere un'interfaccia Gunche definisce una funzione shoot().

Abbiamo bisogno di diverse sottoclassi di Gunclasse, ShotGun Snipere così via.

ShotGun implements Gun{
    public void shoot(){
       \\shotgun implementation of shoot.
    } 
}

Sniper implements Gun{
    public void shoot(){
       \\sniper implementation of shoot.
    } 
}

Classe di tiratori

Il tiratore ha tutte le armi nella sua armatura. Consente di creare un Listper rappresentarlo.

List<Gun> listOfGuns = new ArrayList<Gun>();

Il tiratore scorre ciclicamente le sue pistole, come e quando necessario, usando la funzione switchGun()

public void switchGun(){
    //code to cycle through the guns from the list of guns.
    currentGun = //the next gun in the list.
}

Siamo in grado di impostare la pistola corrente, utilizzando la funzione sopra e semplicemente chiamare la shoot()funzione, quando fire()viene chiamato.

public void fire(){
    currentGun.shoot();
}

Il comportamento della funzione di scatto varierà in base alle diverse implementazioni Gundell'interfaccia.

Conclusione

Creare un'interfaccia, quando una funzione di classe dipende da una funzione di un'altra classe , che è soggetta a modificarne il comportamento, in base all'istanza (oggetto) della classe implementata.

ad es. la fire()funzione della Shooterclasse prevede che le pistole ( Sniper, ShotGun) implementino la shoot()funzione. Quindi se accendiamo la pistola e il fuoco.

shooter.switchGun();
shooter.fire();

Abbiamo cambiato il comportamento della fire()funzione.


1

Espandere ciò che ha detto Larsenal. Un'interfaccia è un contratto che devono seguire tutte le classi di attuazione. Per questo motivo, è possibile utilizzare una tecnica chiamata programmazione al contratto. Ciò consente al software di diventare indipendente dall'implementazione.


1

Le interfacce vengono generalmente utilizzate quando si desidera definire un comportamento che gli oggetti possono mostrare.

Un buon esempio di ciò nel mondo .NET è l' IDisposable interfaccia , che viene utilizzata su tutte le classi Microsoft che utilizzano risorse di sistema che devono essere rilasciate manualmente. Richiede che la classe che lo implementa abbia un metodo Dispose ().

(Il metodo Dispose () viene anche chiamato dal costrutto del linguaggio using per VB.NET e C # , che funziona solo suIDisposable s)

Tieni presente che puoi verificare se un oggetto implementa un'interfaccia specifica usando costrutti come TypeOf ... Is(VB.NET), is(C #), instanceof(Java), ecc ...


1

Poiché probabilmente molte persone hanno già risposto, è possibile utilizzare le interfacce per applicare determinati comportamenti tra le classi che non implementeranno tali comportamenti allo stesso modo. Quindi implementando un'interfaccia stai dicendo che la tua classe ha il comportamento dell'interfaccia. L'interfaccia IAnimal non sarebbe un'interfaccia tipica perché le classi Dog, Cat, Bird, ecc. Sono tipi di animali e probabilmente dovrebbero estenderla, il che è un caso di eredità. Al contrario, un'interfaccia sarebbe più simile al comportamento animale in questo caso, come IRunnable, IFlyable, ITrainable, ecc.

Le interfacce sono buone per molte cose, una delle cose chiave è la connettività. Ad esempio, la dichiarazione di un metodo con un parametro List consentirà il passaggio di tutto ciò che implementa l'interfaccia List, consentendo allo sviluppatore di rimuovere e collegare un altro elenco in un secondo momento senza dover riscrivere una tonnellata di codice.

È possibile che non utilizzerai mai le interfacce, ma se stai progettando un progetto da zero, in particolare un framework di qualche tipo, probabilmente vorrai conoscerli.

Consiglierei di leggere il capitolo sulle interfacce in Java Design di Coad, Mayfield e Kern. Lo spiegano un po 'meglio del testo introduttivo medio. Se non usi Java, puoi semplicemente leggere l'inizio del capitolo, che è principalmente concetti.


1

Come qualsiasi tecnica di programmazione che aggiunge flessibilità al tuo sistema, anche le interfacce aggiungono un certo livello di complessità. Spesso sono fantastici e puoi usarlo ovunque (puoi creare un'interfaccia per tutte le tue classi) - ma così facendo, creeresti un sistema più complesso che sarebbe più difficile da mantenere.

C'è un compromesso qui, come al solito: flessibilità rispetto alla manutenibilità. Qual è più importante? Non ci sono risposte - dipende dal progetto. Ma ricorda che tutti i software dovranno essere mantenuti ...

Quindi il mio consiglio: non usare le interfacce fino a quando non ne hai davvero bisogno. (Con Visual Studio, puoi estrarre un'interfaccia da una classe esistente in 2 secondi, quindi non avere fretta.)

Detto questo, quando è necessario creare un'interfaccia?

Lo faccio quando refactoring un metodo che improvvisamente deve elaborare due o più classi simili. Quindi creo un'interfaccia, assegno questa interfaccia a due (o più) classi simili e cambio il tipo di parametro del metodo (sostituisco il tipo di classe con il tipo di interfaccia).

E funziona: o)

Un'eccezione: quando io quando derido oggetti, l'interfaccia è molto più facile da usare. Quindi spesso creo un'interfaccia solo per questo.

PS: quando scrivo "interfaccia", intendo: "interfaccia di qualsiasi classe di base", comprese le classi di interfaccia pure. Nota che le classi astratte sono spesso una scommessa migliore delle interfacce pure poiché puoi aggiungere logica ad esse.

Saluti, Sylvain.


1

Le interfacce diventeranno evidenti quando diventerai uno sviluppatore di librerie (qualcuno che codifica per altri programmatori). Molti di noi iniziano come sviluppatori di applicazioni , dove utilizziamo API e librerie di programmazione esistenti.

Sulla stessa linea che le interfacce sono un contratto , nessuno ha ancora menzionato che le interfacce sono un ottimo modo per rendere stabili alcune parti del codice . Ciò è particolarmente utile quando si tratta di un progetto di gruppo (o quando si sviluppa codice utilizzato da altri sviluppatori). Quindi, ecco uno scenario concreto per te:

Quando stai sviluppando codice in una squadra , altri probabilmente useranno il codice che scrivi. Saranno molto felici quando codificheranno per le tue interfacce (stabili) e sarai felice quando avrai la libertà di cambiare le tue implementazioni (nascoste dietro l'interfaccia) senza rompere il codice del tuo team. È una variante del nascondere le informazioni (le interfacce sono pubbliche, le implementazioni sono nascoste ai programmatori client). Ulteriori informazioni sulle varianti protette .

Vedi anche questa domanda correlata sulla codifica di un'interfaccia .


1

Ci sono tanti scopi per usare un'interfaccia.

  1. Utilizzare nel comportamento polimorfico. Dove si desidera chiamare metodi specifici di una classe figlio con un'interfaccia con un riferimento alla classe figlio.

  2. Avere un contratto con le classi per implementare tutti i metodi dove è necessario, come l'uso più comune è con gli oggetti COM, dove una classe wrapper viene generata su una DLL che eredita l'interfaccia; questi metodi sono chiamati dietro le quinte e devi solo implementarli ma con la stessa struttura definita nella DLL COM che puoi conoscere solo attraverso l'interfaccia che espongono.

  3. Ridurre l'utilizzo della memoria caricando metodi specifici in una classe. Come se avessi tre oggetti business e fossero implementati in una singola classe, puoi usare tre interfacce.

Ad esempio IUser, IOrder, IOrderItem

public interface IUser()
{

void AddUser(string name ,string fname);

}

// Same for IOrder and IOrderItem
//


public class  BusinessLayer: IUser, IOrder, IOrderItem

{    
    public void AddUser(string name ,string fname)
    {
        // Do stuffs here.
    }

    // All methods from all interfaces must be implemented.

}

Se vuoi solo aggiungere un utente, fai così:

IUser user = new (IUser)BusinessLayer();

// It will load  all methods into memory which are declared in the IUser interface.

user.AddUser();
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.