È una cattiva abitudine non usare le interfacce? [chiuso]


40

Uso le interfacce raramente e le trovo comuni negli altri codici.

Inoltre creo sottoclassi e superclassi (mentre creo le mie classi) raramente nel mio codice.

  • È una brutta cosa?
  • Consiglieresti di cambiare questo stile?
  • Questo stile ha effetti collaterali?
  • È perché non ho lavorato su grandi progetti?

10
Che lingua è questa?

2
Oltre che lingua, che paradigma è questo?
Armando,

1
Il concetto di "interfaccia" non trascende il linguaggio? Java ha il tipo di interfaccia integrata, ma gli sviluppatori C ++ spesso creano anche interfacce (prefissandole con I) e tutte hanno lo stesso scopo.
Ricket,

4
@Ricket: No, non proprio. Il problema è che C ++ offre l'ereditarietà di più classi. In C ++ una "interfaccia" è molto più concettuale e in realtà usiamo principalmente quella che definirebbero una classe base astratta. #
DeadMG

2
@Ricket no, specialmente se stai lavorando in un linguaggio funzionale piuttosto che in un linguaggio procedurale o OOP.
alternativa il

Risposte:


36

Esistono diversi motivi per cui potresti voler utilizzare le interfacce:

  1. Le interfacce sono adatte a situazioni in cui le applicazioni richiedono molti tipi di oggetti possibilmente non correlati per fornire determinate funzionalità.
  2. Le interfacce sono più flessibili delle classi di base perché è possibile definire un'unica implementazione in grado di implementare più interfacce.
  3. Le interfacce sono migliori in situazioni in cui non è necessario ereditare l'implementazione da una classe base.
  4. Le interfacce sono utili nei casi in cui non è possibile utilizzare l'ereditarietà delle classi. Ad esempio, le strutture non possono ereditare dalle classi, ma possono implementare interfacce.

http://msdn.microsoft.com/en-us/library/3b5b8ezk(v=vs.80).aspx

Le interfacce sono come qualsiasi altra cosa nella programmazione. Se non ti servono, non usarli. Li ho visti ampiamente usati come una questione di stile, ma se non hai bisogno delle proprietà e delle capacità speciali fornite da un'interfaccia, non vedo il vantaggio di usarli "solo perché".


13
Il suo linguaggio non è specificato e il tuo è troppo specifico, ad esempio, in C ++, una struttura può effettivamente ereditare da una classe e puoi moltiplicare ereditare basi non astratte.
DeadMG

2
Questa risposta sembra essere C # ma riguarda anche Java se si estrae il numero 4, e solo il tipo di applicazione si applica al C ++ perché ovviamente l'ereditarietà multipla è consentita in C ++.
Ricket,

2
Inoltre, non sono d'accordo con l'ultima affermazione. Penso che ci siano molte buone pratiche che i programmatori dovrebbero fare ma non perché sentono di non averne bisogno. Penso che il richiedente sia molto saggio nel guardarsi intorno, nel vedere tutti gli altri che fanno questa cosa e nel pensare che forse dovrebbe farlo anche lui, ma prima chiedendo "perché".
Ricket,

3
@Ricket: il motivo è nei quattro punti elenco della mia risposta. Se nessuno di questi motivi si applica, non è necessaria un'interfaccia.
Robert Harvey,

17

Sia l'eredità di classe che le interfacce hanno entrambe il loro posto. Eredità significa "è un" mentre un'interfaccia fornisce un contratto che definisce come "qualcosa si comporta".

Direi che usare le interfacce più spesso non è affatto una cattiva pratica. Attualmente sto leggendo "Efficace C # - 50 modi specifici per migliorare il tuo C #" di Bill Wagner. L'articolo numero 22 indica, e una citazione I, "Preferire definire e implementare le interfacce all'ereditarietà".

In genere utilizzo le classi di base quando devo definire un'implementazione specifica del comportamento comune tra tipi concettualmente correlati. Più spesso uso interfacce. In effetti, normalmente inizio definendo un'interfaccia per una classe quando inizio a crearne una ... anche se alla fine non compilo l'interfaccia, trovo che aiuti iniziare iniziando definendo l'API pubblica del classe dall'inizio. Se trovo che ho più classi sia che implementano l'interfaccia, sia che la logica di implementazione è identica, solo allora mi chiederò se avrebbe senso implementare una classe base comune tra i tipi.

Un paio di citazioni dal libro di Bill Wagners ...

tutte le classi derivate incorporano immediatamente quel comportamento. L'aggiunta di un membro a un'interfaccia interrompe tutte le classi che implementano tale interfaccia. Non conterranno il nuovo metodo e non verranno più compilati. Ogni implementatore deve aggiornare quel tipo per includere il nuovo membro. Scegliere tra una classe base astratta e un'interfaccia è una domanda sul modo migliore di supportare le tue astrazioni nel tempo. Le interfacce sono fisse: rilasci un'interfaccia come contratto per un set di funzionalità che qualsiasi tipo può implementare. Le classi di base possono essere estese nel tempo. Tali estensioni diventano parte di ogni classe derivata. I due modelli possono essere combinati per riutilizzare il codice di implementazione supportando più interfacce. " Non conterranno il nuovo metodo e non verranno più compilati. Ogni implementatore deve aggiornare quel tipo per includere il nuovo membro. Scegliere tra una classe base astratta e un'interfaccia è una domanda sul modo migliore di supportare le tue astrazioni nel tempo. Le interfacce sono fisse: rilasci un'interfaccia come contratto per un set di funzionalità che qualsiasi tipo può implementare. Le classi di base possono essere estese nel tempo. Tali estensioni diventano parte di ogni classe derivata. I due modelli possono essere combinati per riutilizzare il codice di implementazione supportando più interfacce. " Non conterranno il nuovo metodo e non verranno più compilati. Ogni implementatore deve aggiornare quel tipo per includere il nuovo membro. Scegliere tra una classe base astratta e un'interfaccia è una domanda sul modo migliore di supportare le tue astrazioni nel tempo. Le interfacce sono fisse: rilasci un'interfaccia come contratto per un set di funzionalità che qualsiasi tipo può implementare. Le classi di base possono essere estese nel tempo. Tali estensioni diventano parte di ogni classe derivata. I due modelli possono essere combinati per riutilizzare il codice di implementazione supportando più interfacce. " Rilasci un'interfaccia come contratto per un set di funzionalità che qualsiasi tipo può implementare. Le classi di base possono essere estese nel tempo. Tali estensioni diventano parte di ogni classe derivata. I due modelli possono essere combinati per riutilizzare il codice di implementazione supportando più interfacce. " Rilasci un'interfaccia come contratto per un set di funzionalità che qualsiasi tipo può implementare. Le classi di base possono essere estese nel tempo. Tali estensioni diventano parte di ogni classe derivata. I due modelli possono essere combinati per riutilizzare il codice di implementazione supportando più interfacce. "

"Le interfacce di codifica offrono una maggiore flessibilità agli altri sviluppatori rispetto alla codifica per tipi di classe base."

"L'uso delle interfacce per definire le API per una classe offre una maggiore flessibilità."

"Quando il tuo tipo espone le proprietà come tipi di classe, espone l'intera interfaccia a quella classe. Usando le interfacce, puoi scegliere di esporre solo i metodi e le proprietà che desideri che i client utilizzino."

"Le classi di base descrivono e implementano comportamenti comuni tra tipi concreti correlati. Le interfacce descrivono elementi atomici di funzionalità che i tipi concreti non correlati possono implementare. Entrambi hanno il loro posto. Le classi definiscono i tipi che crei. Le interfacce descrivono il comportamento di quei tipi come elementi di funzionalità. Se capisci le differenze, creerai progetti più espressivi che sono più resistenti di fronte al cambiamento. Usa le gerarchie di classi per definire i tipi correlati. Esponi la funzionalità usando le interfacce implementate in quei tipi. "


2
Bene, l'ho letto e ho pensato che fosse una buona risposta.
Eric King,

1
@ mc10 Non sono sicuro di quale sia il problema. Ha fatto riferimento a un libro e la metà inferiore della sua risposta è solo citazioni da quel libro che ti fa chiaramente sapere nel caso in cui non vuoi perdere tempo.
Pete,

@Pete Non ho detto che la risposta è negativa, ma solo che è stato un po 'troppo lungo. Dubito che a volte ti servisse l'intera citazione.
kevinji,

3
ahah, se questo è tl; dr, non sono sicuro che ti piacerà tutta questa cosa di StackExchange con risposte di alta qualità.
Nic

ahah, grazie ragazzi. Sì, mi è venuto in mente che la risposta era un po 'prolissa, ma ho pensato di qualificare la mia risposta includendo le citazioni pertinenti del sig. Wagner che spiegavano i vantaggi e gli svantaggi delle interfacce rispetto all'eredità, nonché gli scenari in cui ognuno è più appropriato. Forse la citazione diretta del libro non ha aggiunto molto valore alla mia risposta.
John Connelly,

8

Una cosa che non è stata menzionata è il testing: in tutte le librerie di simulazione di C #, così come in alcune per Java, le classi non possono essere derise se non implementano un'interfaccia. Questo porta molti progetti seguendo le pratiche Agile / TDD per dare ad ogni classe la propria interfaccia.

Alcune persone considerano questa best practice, perché "riduce l'accoppiamento", ma non sono d'accordo - penso che sia solo una soluzione alternativa a una carenza della lingua.


Penso che le interfacce vengano utilizzate al meglio quando hai due o più classi che, astrattamente, fanno la "stessa cosa", ma in modi diversi.

Ad esempio, il framework .Net ha più classi che memorizzano elenchi di cose , ma tutte archiviano quelle cose in modi diversi. Pertanto, ha senso disporre di un'interfaccia astratta IList<T>, che può essere implementata utilizzando metodi diversi.

Dovresti anche usarlo quando vuoi che 2+ classi siano intercambiabili o sostituibili in futuro. Se in futuro verrà fuori un nuovo modo di memorizzare elenchi AwesomeList<T>, supponendo che tu abbia usato IList<T>tutto il tuo codice, cambiarlo per usarlo AwesomeList<T>significherebbe solo cambiare alcune dozzine di righe, anziché alcune centinaia / migliaia.


5

Il risultato principale di non usare l'ereditarietà e le interfacce quando sono appropriate è l' accoppiamento stretto . Questo può essere un po 'difficile da imparare a riconoscere, ma di solito il sintomo più evidente è quando fai una modifica, scopri che devi spesso passare attraverso un sacco di altri file per apportare modifiche in un effetto a catena.


4

No, questo non è affatto male. Le interfacce esplicite dovrebbero essere utilizzate, se e solo se, due classi con un'interfaccia comune devono essere intercambiabili in fase di esecuzione. Se non hanno bisogno di essere intercambiabili, non farli ereditare. E 'così semplice. L'ereditarietà è fragile e dovrebbe essere evitata laddove possibile. Se puoi evitarlo o usare invece generici, allora fallo.

Il problema è che, in linguaggi come C # e Java con generici in fase di compilazione deboli, si può finire per violare DRY perché non c'è modo di scrivere un metodo in grado di gestire più di una classe a meno che tutte le classi ereditino dalla stessa base. C # 4 dynamic può farcela, però.

Il fatto è che l'eredità è come una variabile globale: una volta che l'hai aggiunta e il tuo codice dipende da essa, Dio ti aiuta a toglierla. Tuttavia, puoi aggiungerlo in qualsiasi momento e puoi anche aggiungerlo senza alterare la classe base usando una sorta di wrapper.


1
Motivo del downvote?
DeadMG

Non sono sicuro, ho un voto. È stata una buona risposta
Bryan Boettcher,

3

Sì, non (o troppo raramente) usare le interfacce è probabilmente una cosa negativa. Le interfacce (in senso astratto, e il costrutto del linguaggio C # / Java è una buona approssimazione di quel senso astratto) definiscono punti di interazione espliciti tra sistemi e sottosistemi. Ciò aiuta a ridurre l'accoppiamento e rende il sistema più gestibile. Come per tutto ciò che migliora la manutenibilità, diventa tanto più importante quanto più grande è un sistema.


3

Non uso un'interfaccia da anni. Naturalmente questo è perché sto programmando quasi esclusivamente in Erlang ormai da anni e l'intero concetto di un'interfaccia semplicemente non esiste. (Il più vicino che ottieni è un "comportamento" e non è proprio la stessa cosa a meno che non strizzi gli occhi e li guardi con la coda dell'occhio.)

Quindi, davvero, la tua domanda dipende dal paradigma (OOP in questo caso) e, inoltre, è davvero piuttosto dipendente dalla lingua (ci sono lingue OOP senza interfacce).


2

Se stai parlando di usare Java, uno dei motivi per usare le interfacce è che abilitano l'uso di oggetti proxy senza librerie di generazione del codice. Questo può essere un vantaggio sostanziale quando si lavora con un framework complesso come Spring. Inoltre, alcune funzionalità richiedono interfacce: RMI è il classico esempio di questo, in quanto è necessario descrivere la funzionalità che si sta fornendo in termini di interfacce (che ereditano da java.rmi.Remote) indipendentemente dall'implementazione.


1

Le cose stanno cambiando ( MS Moles ), ma il motivo principale per cui penso che sia buono programmare quasi esclusivamente le interfacce è che sono facili da deridere e si adattano naturalmente a un'architettura IoC.

IMO dovresti sempre lavorare con interfacce o DAO completamente stupidi, ove possibile. Quando entri in questa mentalità e inizi a utilizzare una libreria che non si espone attraverso le interfacce e fa tutto attraverso oggetti concreti, devo essere sincero, tutto sembra un po 'goffo.


0

Per i vecchi progetti di moda, si usano le interfacce per evitare riferimenti circolari, perché i riferimenti circolari potrebbero diventare enormi problemi di manutenzione a lungo termine.

Male:

class B; // forward declaration
class A
{
  B* b;
};

class B
{
  A* a;
}

Non male:

class PartOfClassB_AccessedByA
{
};

class A
{
  PartOfClassB_AccessedByA* b;
};

class B : public PartOfClassB_AccessedByA
{
  A* a;
}

Di solito A, B, PartOfClassB_AccessedByA implementato con file separati.


0

La programmazione basata su interfaccia aiuta a rendere il codice più flessibile e più facile da testare. Le classi di implementazione possono essere modificate senza toccare il codice client [Flessibilità]. Durante il test del codice si può sostituire la programmazione basata su oInterface per rendere il codice più flessibile e più facile da testare. Le classi di implementazione possono essere modificate senza toccare il codice client [Flessibilità]. Durante il test del codice si può sostituire l'oggetto reale con oggetti finti [Testabilità] .bject con oggetti finti [Testabilità].

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.