2009-02-20 16 views
27

(nel contesto di .NET per quello che vale)Quando sono necessarie le interfacce?

Tendo a non utilizzare l'ereditarietà e raramente utilizzo le interfacce. Mi sono imbattuto in qualcuno che pensa che le interfacce siano la cosa migliore dopo lo sputo. Li usa ovunque. Non capisco questo e quindi le domande che seguono. Voglio solo un controllo sulla mia comprensione delle interfacce.

Se si utilizzano le interfacce in tutto il mondo, presumo che sia possibile prevedere il futuro, i requisiti delle app sono inchiodati e nulla cambierà mai nella tua app. Per me, soprattutto durante lo sviluppo iniziale, le interfacce diventano un ostacolo. L'app è molto dinamica attraverso la sua vita. Se devi sottrarre o aggiungere membri all'interfaccia, molte cose si romperanno. Il tizio sopra dice che crea un'altra interfaccia per gestire i nuovi membri. Niente si rompe

Non è quella composizione? Perché non usare la composizione senza interfacce? Più flessibile.

Come gestisce il caso in cui un membro deve essere sottratto dall'interfaccia? Fondamentalmente lui no. Le cose si rompono e questo è fantastico perché ora puoi vedere tutte le aree interessate e risolverle. Invece di capire in modo più elegante dove sono tutti i percorsi di codice correlati, dovremmo semplicemente strappare parti di classi attraverso la forza bruta?

Penso a un'applicazione software come un grafico. Un grafico completo è il caso peggiore, avendo n (n-1)/2. Ciò significa che ogni classe parla con ogni classe. Una ragnatela confusa. n-1 è il migliore, in cui la loro è una rigida gerarchia di comunicazione. L'aggiunta di un'altra interfaccia solo per compensare un nuovo membro necessario aggiunge un vertici al grafico, il che significa più margini e una più forte realizzazione dell'equazione n (n-1)/2. La composizione senza interfacce è più simile a mixins. Solo le classi selezionate usano metodi particolari. Con un'interfaccia, tutte le classi sono obbligate a utilizzare i membri, anche se non ne hanno bisogno. L'approccio composizione/mixin non aggiunge nuovi bordi non necessari.

+1

Devo essere in disaccordo con alcune delle vostre ipotesi. Le interfacce e l'ereditarietà hanno i loro usi e il refactoring dell'applicazione nel corso della sua vita è prevedibile. "Perché non usare la composizione senza interfacce?" Perché puoi testare elegantemente se gli oggetti disparati espongono un'interfaccia. – overslacked

+1

In realtà l'analisi del grafico è giusta ma la tua conclusione è sbagliata. Se hai n chiamanti che chiamano m callees hai n * m bordi. Se tutte queste chiamate possono essere eseguite tramite un'interfaccia singola (ottimistica ma plausibile), allora hai solo bordi n + m. La distribuzione efficace delle interfacce ridurrà notevolmente la complessità del grafico. – CurtainDog

+0

"... tutte le chiamate possono essere controllate attraverso un'unica interfaccia ..." Forse in un'applicazione molto semplice. "... allora hai solo bordi n + m."Se l'interfaccia è ereditata da classi che usano tutti i membri dell'interfaccia, allora è una lavata. Il mio scenario si riferisce al caso che impazzisci con le interfacce Leggi ancora l'ultima frase – 4thSpace

risposta

40

Le interfacce non impongono le classi a utilizzando i metodi. Costringono le classi di implementazione a a implementare tutti i metodi,, ma è diverso.

Mi piace il modo in cui le interfacce separano l'API dall'implementazione. Ammettiamo che questo sia fatto anche con i modificatori di accesso, ma le interfacce lo rendono più chiaro. Ancora più importante, le interfacce semplificano anche il mocking, il che significa che puoi testare le classi che dipendono dall'interfaccia prima ancora che tu l'abbia mai implementata.

E sì, questo significa che spesso finisco con un'interfaccia che ha una sola implementazione di produzione. Dal mio punto di vista non è un problema, perché ho guadagnato la testabilità.

D'altra parte, non scrivo un'interfaccia per ogni classe. Mi piace scrivere interfacce in cui un oggetto fornisce essenzialmente un servizio - autenticazione, accesso ai dati, ecc. Oggetti di dati semplici (anche con un comportamento significativo) non sono così utili in termini di interfacce, IME.

+0

Grazie alla definizione chiarita nel tuo primo paragrafo. Queste classi non sono esposte a terze parti o come servizi Web. Si applica l'ultima frase. Che cosa intendevi per "... ha una singola implementazione di produzione?" – 4thSpace

+0

"ha una singola implementazione di produzione" = "Esiste una sola implementazione di l'interfaccia nel codice di non test. " –

+0

Ancora non seguendo ciò che intendi sopra.Sembra che tu stia molto attento all'uso delle interfacce in generale. – 4thSpace

7

Secondo wikipedia,

L'uso primario del polimorfismo nell'industria (orientata agli oggetti teoria programmazione) è la capacità di oggetti appartenenti a diverse tipologie per rispondere agli metodo, campo, o proprietà chiamate con lo stesso nome , ciascuno secondo un comportamento specifico del tipo appropriato. Il programmatore (e il programma) non deve conoscere in anticipo il tipo esatto dell'oggetto, e quindi il comportamento esatto viene determinato in fase di esecuzione (questo è chiamato associazione tardiva o associazione dinamica).

Questo è ciò che rende le interfacce così utili.

"Il ragazzo sopra dice che crea un'altra interfaccia per gestire i nuovi membri.

Sto solo indovinando qui, ma sembra che questo ragazzo provenga da uno sfondo della vecchia scuola COM (potrei sbagliarmi, ovviamente!). Mi fa rabbrividire a pensare a tutto il codice che ho lavorato su dove ho visto cose del genere:

  • IWidgetManager
  • IWidgetManager2
  • IWidgetManager3

Questo non è un buon modo lavorare con le interfacce. Nella mia esperienza, ho visto entrambi gli estremi: la paura di cambiare le interfacce fino al punto in cui nuove interfacce vengono create ogni volta che vengono aggiunti nuovi membri o non si utilizzano affatto interfacce e si dispone di un prodotto che soffre di un elevato grado di coupling. Devi trovare un buon equilibrio. Non è sempre la fine del mondo a cambiare un'interfaccia.

Qual è la dimensione del progetto su cui stai lavorando? Può essere difficile vedere il vantaggio delle interfacce se si tratta di un progetto di dimensioni relativamente ridotte. Se, d'altra parte, si tratta di un progetto con diverse centinaia di migliaia di righe di codice ed è composto da molti moduli, i vantaggi diventano molto più evidenti.

+0

Non sono sicuro di cosa intendi con "Questo è ciò che rende le interfacce così deliziose" o la tua prima frase. Non seguendo come hai dedotto una conclusione del genere con le informazioni che ho fornito. Sto sottilmente facendo notare che le interfacce non sono un martello per molto chiodo, che non ha nulla a che fare con il polimorfismo. – 4thSpace

+1

È possibile ottenere il polimorfismo attraverso l'ereditarietà senza utilizzare alcuna interfaccia. Non sono un prerequisito per il polimorfismo. –

1

Le interfacce hanno più senso per me nel contesto dell'integrazione delle dipendenze e dei framework IoC. Mi piace l'idea che sia possibile definire un insieme di comportamenti (metodi) e "garantire" tali comportamenti attraverso l'implementazione di un'interfaccia. Ora è possibile collegare tutte le nuove funzioni in un sistema esistente con un singolo assemblaggio e un aggiornamento del file di configurazione.

L'efficace progettazione di interfacce richiede una buona pianificazione in avanti e trovo che siano più utili nel contesto di sistemi e framework di grandi dimensioni. Quando sono utili sono veramente utili. Alcuni dei miei preferiti: (? LINQ chiunque)

  • IComparable (si decide come gli oggetti confrontano uno contro l'altro)
  • IQueryable
  • IDisposable (tenere conto della tua using a portata di mano)
+0

Sì ed è una buona descrizione di quando applicare. Un framework di grandi dimensioni è simile a un'app personalizzata che espone la sua API, il che è meglio farlo attraverso un'interfaccia. – 4thSpace

5

Le interfacce hanno molte situazioni utili. Quando è necessario aggiungere un comportamento specifico a una classe che può essere trovata in un'ampia varietà di classi, è un momento perfetto per un'interfaccia. Un buon esempio è l'interfaccia IDisposable - hai alcune risorse che quando hai finito, devono andare via in modo tempestivo. È una connessione al database? È un po 'di maniglia della finestra? Non importa

Un altro esempio potrebbe essere quando davvero non è sapere come dovrebbe essere implementato, ad esempio un'interfaccia per un oggetto che non esiste ancora. Forse l'oggetto dovrebbe essere fornito da un client della tua libreria, o deve essere implementato da un modulo completamente diverso non sotto il tuo controllo. Puoi fondamentalmente progettare un contratto per i metodi disponibili sulla classe.

Detto questo, li uso solo dove è necessario.Se riesco a farlo con una lezione normale, o se è qualcosa di intrinseco all'oggetto particolare, lo renderò una classe. Ci sono alcuni vantaggi nell'usare le interfacce per ogni classe, come hanno detto altri, ma questo è un sovraccarico in più che non vedo un discreto guadagno netto su di esso. Il più delle volte, ho progettato le mie strutture di classe in modo che siano piatte e ampie, con il minor numero possibile di dipendenze.

In breve: se si richiedono funzionalità comuni implementate in modo radicalmente diverso, un'interfaccia è proprio ciò di cui si ha bisogno.

0

Penso che si descriva il metodo top-down design.
Non devi farlo, ma a volte aiuta davvero.

2

"Il ragazzo di cui sopra dice egli crea un'altra interfaccia per gestire il nuovo membro (s). Nulla si rompe.

Non è che la composizione? Perché non utilizzare composizione senza interfacce? Più flessibile."

Sembra che tu stia pensando alla composizione in termini di ereditarietà, come in "Io erediterò tutte queste capacità in questo unico oggetto per fare questo lavoro". È un brutto modo di scrivere codice. È come dire: "Voglio costruire un grattacielo, quindi imparerò ogni lavoro che c'è da sapere, da come creare i progetti fino a come ridurre le fondamenta e installare l'illuminazione ..."

Pensa invece alla composizione in termini di oggetti separati che eseguono ciascuno un singolo compito. Per fare un lavoro complesso, ora posso fare affidamento su questi oggetti separati per eseguire i loro compiti individuali. È come assumere l'architetto e l'equipaggio per costruire il grattacielo. Non ho bisogno di sapere in dettaglio come ognuno di loro fa il proprio lavoro, solo che può farlo. Nel codice, ciò significa iniettare dipendenze in un oggetto per eseguire compiti complessi invece di ereditare.

Quindi dove inserire le interfacce? Sono il contratto tra i singoli oggetti. Ti permettono di non preoccuparti delle implementazioni individuali e concrete. Ti consentono di parlare un linguaggio comune con un gruppo di oggetti che condividono la stessa responsabilità, ma potrebbero effettivamente avere un'implementazione diversa. L'interfaccia diventa un'astrazione per l'oggetto che esegue l'attività. Non ho bisogno di sapere come funziona ogni singolo ragazzo con un martello, solo che sa come HitNail().

Quando si inizia a comporre un sistema complesso di codice con un sacco di classi piccole che hanno responsabilità singole, si impara presto che si possono incorrere in problemi seri se si fa troppo affidamento sull'implementazione concreta di una determinata classe e la classe inizia a cambiare ... Quindi, invece di fare affidamento sull'implementazione concreta, creiamo un'interfaccia - un'astrazione. E noi diciamo: "Non mi interessa come fai a JobX, solo che lo fai".

Ci sono altri vantaggi per interfacce come testing, mocking, ecc ... Ma questi non sono il motivo per codificare un'interfaccia. La ragione è di creare un sistema di codice che non dipende da specifiche e quindi altamente accoppiati tra loro. Questo è il motivo per cui, con il tuo cervello di pensiero grafico, dovresti aver paura di accoppiare un gruppo di lezioni concrete l'uno con l'altro. Perché i cambiamenti in una di quelle classi causeranno un effetto a catena.

Quando si abbina a un'astrazione anziché a una classe concreta, si limita l'accoppiamento. Dici, io sarò solo abbinato al contratto su cui entrambi siamo d'accordo, e nient'altro. " E se la classe che implementa quel contratto cambia il suo modo interno di completare il suo compito, non ti interessa, perché non stai facendo affidamento su una proprietà o un metodo senza contratto. Stai facendo affidamento solo sul contratto concordato.

+0

"Sembra che tu stia pensando alla composizione in termini di eredità ..." - No. Come accennato, raramente eredito. Stavo pensando in termini di piccole classi che espongono un piccolo numero di membri. Altre classi compongono queste classi più piccole esponendole attraverso un metodo/una proprietà. Meglio dell'interfaccia? – 4thSpace

2

Se si desidera capire quando utilizzare le interfacce e quale è il loro utilizzo, penso che si dovrebbe dare un'occhiata al libro: Head First Desing Patterns.

Questo è il libro che mi ha davvero aiutato a capire cosa è interessante sulle interfacce.

Prima di leggere questo libro, sapevo cosa fosse un'interfaccia, ma non avevo assolutamente idea di quando dovrei usarli.

+0

Questo è un ottimo libro per dimostrare interfacce. –

+0

In effetti, questo è solo un ottimo libro che penso che ogni programmatore dovrebbe leggere: P Questo ha davvero cambiato il mio modo di programmare/pensare alla programmazione, qualcosa che avrei voluto aver imparato a scuola. – Martin

+1

Mi piace anche aggiungere Head First Oriented Object Analysis and Design, anche questo è un bel libro. – Ali

0

mi piacerebbe fare riferimento alle linee guida di progettazione .net su questo: http://msdn.microsoft.com/en-us/library/ms229013.aspx

Ci sono un sacco di motivi per utilizzare le interfacce, ma ho anche trovato che sono spesso abusato per le ragioni sbagliate. Le interfacce offrono molta più flessibilità quando si lavora con i tipi di valore e sono incredibilmente utili quando si lavora con collezioni, ecc, ma quando si progetta una gerarchia di classi per i miei progetti, cerco sempre di pensare prima alla semplicità e le interfacce spesso portano (inutilmente) situazioni più complesse.

La mia regola empirica è quella di implementare ogni interfaccia BCL che abbia senso, e aggiungere le mie interfacce ai miei progetti solo quando effettivamente fornisce qualcosa di molto prezioso. Invece di avere IWidgetManager, IWidgetManager2, ecc ... preferirei avere una classe astratta WidgetManager e implementare metodi concreti secondo le necessità.

2

Si tratta di dipendenze. Si desidera che il codice dipenda dai tipi di interfaccia anziché dai tipi di calcestruzzo, ove appropriato. Ad esempio, potresti avere più tipi di calcestruzzo che eseguono tutti un comportamento simile ma lo implementano in modo diverso. Vuoi che il tuo codice base abbia dipendenze da questi tipi concreti multipli o da un'interfaccia? Quanto pensi sia flessibile la tua base di codice quando hai bisogno di apportare modifiche?

Per il tuo punto di non voler usare membri pubblici utilizzando la composizione, direi che stai solo incapsulando dipendenze non necessarie. Se si desidera utilizzare la composizione, si riducono notevolmente le dipendenze componendo tipi di interfaccia anziché tipi concreti.

Per una spiegazione migliore, provare a guardare la letteratura su inversion of control.

0

Questa persona sembra aver frainteso il concetto di programmazione in un'interfaccia. Non si riferisce alla programmazione usando solo la parola chiave interface in .Net/Java o classe con nient'altro che funzioni virtuali pure in C++, L'interfaccia a cui si fa riferimento in quel principio è un costrutto di alto livello che incapsula il livello basso quelli di un sistema. Come con la moltiplicazione, incapsula l'idea di aggiungere un numero a se stesso a una quantità specifica di ripetizioni. Ma quando diciamo 3 * 2 non ci importa se è 3 + 3, 2 + 2 + 2 o (2 + 2) + 2 dove la parte tra parentesi è memorizzata nella cache. Finché riceviamo un 6 da esso.

In effetti il ​​concetto di interfacce può essere riempito da un interface o abstract class o una combinazione di questi come nel caso di molti dei modelli GoF. È solo che il tipo di parola chiave interface oscura l'acqua con ambiguità.

È divertente. Il tipo di pensiero di questo tizio è probabilmente quello che ha generato commenti come quelli che ruotano attorno all'episodio # 38 di StackOverflow.

3

Le interfacce aiutano a mantenere le dipendenze dalle astrazioni.

Il codice che utilizza l'interfaccia dipende solo dall'interfaccia, in modo da sapere che non ci sono dipendenze artificiali sui dettagli. Questo ti dà molta libertà per quanto riguarda la modifica del codice in futuro, dal momento che sai esattamente cosa dovrebbe & non doversi interrompere quando si "aggiusta" un errore o un refactoring.

A mio parere, è l'essenza del buon design del codice.

+0

il tuo tizio scatenato. il suo punto è che le interfacce vengono messe in atto * prima che l'API sia realmente nota * - questo è un modo davvero inappropriato per usare le interfacce. Sono un contratto: firmate i contratti senza capire a fondo cosa vi state dedicando? – alchemical

0

Sembra che il tuo amico stia utilizzando le interfacce in modo errato.

Ho anche visto applicazioni Web con interfacce dappertutto. Alcune persone sembrano avere l'idea che un'interfaccia sia in qualche modo migliore della semplice firma di un metodo. I parametri semplici ci forniscono già un contratto: nella maggior parte dei casi, ritengo che sia sufficiente e rende il codice più semplice.

Mi sembra che le interfacce siano più utili in casi come IDisposable o ICollection - dove indicano un determinato insieme di funzionalità da aspettarsi da un oggetto. In questi casi sembrano essere lo strumento giusto per il lavoro.

0

Utilizziamo intensamente le interfacce sugli elementi dell'interfaccia utente personalizzati, in cui ognuno si aspetta che un determinato metodo esista su un altro (e l'interfaccia ne impone l'esistenza).

0

L'uso principale di un'interfaccia è di consentire la variazione di implementazione che consente di commutare il codice in fase di runtime. Ci sono una serie di ragioni/motivazione per farlo.

In passato alcuni hanno sostenuto che ogni classe nel sistema dovrebbe avere un'interfaccia, ma questo è ampiamente riconosciuto come eccessivo. Le interfacce vengono ora utilizzate dove le istanze delle classi possono essere modificate come parte dell'operazione di sistema (per rappresentare lo stato): i modelli GoF come Strategy e Command acquisiscono questo uso e/o parti del sistema devono essere sostituite per il test (es. iniezione). Se gli sviluppatori seguono pratiche di sviluppo basate su test, gli oggetti dell'infrastruttura principale avranno interfacce. Ciò consente ai test di creare "mock" di questi oggetti per testare il flusso di controllo del sistema. Esiste una relazione tra questo uso dell'interfaccia e il Principio di sostituzione di Liskov (uno dei principi di progettazione OO)

Un ulteriore utilizzo per le interfacce che è meno interessato a ciò che è disponibile per il chiamante, è l'interfaccia marker. Questo è un modo per associare i metadati alla definizione della classe. Ciò potrebbe influire sul modo in cui il sistema visualizza l'oggetto (ad esempio, consentire la serializzazione) o potrebbe semplicemente servire come documentazione.

0

Uno dei motivi più importanti per l'utilizzo di un'interfaccia è il fatto che ci consentono di scrivere test unitari per le nostre classi e di passare nelle nostre dipendenze. Mettendo la dipendenza dietro un'interfaccia apriamo "Cuciture" nel nostro codice applicativo, dove è facile scrivere i test unitari. Non vedo questo angolo di prova menzionato in molte delle risposte, ma è molto importante capire che senza interfacce, queste dipendenze (come un servizio Web o un riferimento al file system) possono diventare molto difficili da testare o nel migliore dei casi sono abbastanza condizionati Ho scritto un post qui: http://jeffronay.com/why-use-an-interface/ che va molto più in dettaglio con esempi di codice che mostrano una classe di servizio senza un'interfaccia, e quindi la stessa classe riscritta usando un'interfaccia per dimostrare la flessibilità del test quando si usano le interfacce.

Problemi correlati