Ogni classe che scrivo dovrebbe aderire a un'interfaccia?


10

Sto scrivendo un gioco in Typescript, e ho deciso di provare ad aderire all'idea di " programmazione basata su interfaccia ", in cui si scrive codice basato su un'interfaccia, anziché sull'implementazione, di un oggetto.

Ho scritto un buon numero di interfacce e classi che le implementano, poi ho fatto un passo indietro e ho capito che le classi erano abbastanza semplici che probabilmente non avrò mai bisogno di cambiare l'implementazione, dal momento che c'è davvero solo un modo per fare ciò che il classe fa (muovendosi Phaser.Spritein modo limitato per comportarsi come un carro armato).

Poi ricordo di aver letto qualche anno fa l'idea di YAGNI , che in pratica non dovrebbe sovra-ingegnerizzare il tuo codice per includere cose che non potresti mai usare.

Seguendo le migliori pratiche, ogni classe dovrebbe implementare un'interfaccia o limitarla a classi che si prevede possano essere scambiate in futuro?


Ricorda, quando usi una classe reale come parametro, stai anche codificando un'interfaccia, l'interfaccia di questa classe. Nessuno ti obbliga a utilizzare quella classe specifica, potresti ereditarla e fornire funzionalità completamente nuove, ma ciò non sembra giusto. Esistono interfacce per rendere le persone più facili da capire. Essendo triste, no, non hai davvero bisogno di interfacce per tutto.
Andy,

Risposte:


6

La ragione per avere interfacce è perché semplifica il polimorfismo. Ciò significa che è possibile inviare istanze per contratto anziché conoscerne l'implementazione effettiva. Ad esempio, è possibile inviare un "Lettore" a un metodo, in modo che tale metodo chiamato possa utilizzare il metodo "read ()". Dichiarando un'interfaccia "Reader" è possibile rendere qualsiasi oggetto conforme al presente contratto, implementando i metodi che specifica. In questo modo, qualsiasi chiamante può presumere che esistano determinati metodi, anche se gli oggetti sottostanti al contratto possono essere completamente diversi.

Questo separa il polimorfismo da una classe base e rende possibile anche tra i confini della catena di classe, implicando un contratto.

Ora ... Questo è utile solo se hai bisogno di più catene di ereditarietà per agire allo stesso modo, o se avrai molte classi di base con un comportamento comune che vorresti passare ad altri utenti.

Se hai solo UNA catena di ereditarietà (baseclass-> sottoclasse) o solo la base, o non hai bisogno di PASSARE effettivamente gli oggetti correlati a qualcun altro o usarli genericamente, allora non aggiungeresti implementazioni di interfaccia, come è allora l'idea inutili.


Aggiungo anche che le interfacce dovrebbero modellare le astrazioni, quindi non crearle semplicemente sollevando tutti i membri pubblici di una classe. Pensa a cosa rappresenta quella classe e metti solo quelle cose nell'interfaccia che dovrebbe avere qualsiasi oggetto di quel tipo. Potresti anche finire di creare più interfacce (ad esempio Movese Shootsper il tuo esempio di serbatoio) per una singola classe.
TMN,

7

Se non sei sicuro di aver effettivamente bisogno delle interfacce, probabilmente non lo fai. Questo è il nucleo del principio YAGNI. Quando è necessario essere in grado di scambiare l'implementazione, si introduce un'interfaccia.


6

La creazione di un'interfaccia per ogni classe è un po 'eccessiva. Da una prospettiva puramente orientata agli oggetti, ogni classe ha già un'interfaccia . Un'interfaccia non è altro che i metodi rivolti al pubblico e i membri dei dati di una classe.

Generalmente usiamo "interfaccia" per indicare "una classe Java definita usando la interfaceparola chiave" o "una classe C ++ con solo funzioni virtuali pubbliche e pure". Queste sono astrazioni utili, perché separano l'interfaccia di una classe dalla sua implementazione.

Tenendo presente ciò, utilizzare le interfacce quando appropriato.

  • Ci saranno implementazioni multiple per un'interfaccia? In tal caso, questa situazione potrebbe essere un candidato per l'estrazione di metodi comuni e pubblici in un'interfaccia.

  • Consegnerò le implementazioni di una potenziale interfaccia ad altri moduli che non dovrebbero conoscere il funzionamento interno del mio modulo? Un'interfaccia può essere utile qui, perché riduce la dimensione del punto di contatto tra i moduli.

Nota le parole della donnola sopra: "può" essere un candidato, "può essere" utile. Non esiste una regola rigida che dice "sì" o "no" per una determinata situazione.

Detto questo, avere un'interfaccia per tutto è molto probabilmente eccessivo. Se vedi questo schema, stai andando lontano:

interface I {
  int getA()
  int getB()
  ...
  int getY()
  int getZ()
}

class C : I {
  int getA() { ... }
  int getB() { ... }
  ...
  int getY() { ... }
  int getZ() { ... }
}

Un motivo in più per le interfacce è se il sistema di test lo rende necessario, il che può dipendere dalla lingua e dal framework di test. Spero non sia necessario farlo, ovviamente.
Darien,

@Darien: il motivo per cui un sistema di test può richiedere interfacce è perché il test sta effettivamente utilizzando un'implementazione diversa (derisa), riportandoti al primo punto in cui un'interfaccia è appropriata.
Bart van Ingen Schenau,

Alcune cose fantastiche qui. Stavo solo riflettendo su questo: un'interfaccia non è altro che i metodi rivolti al pubblico e i membri dei dati di una classe . Sebbene ciò sia vero per un'implementazione dell'interfaccia , certamente non vale per un'interfaccia che non viene mai implementata, anche se concessa - sarebbe una sorta di odore di codice.
Robbie Dee,

@RobbieDee è vero, ad esempio, per un Java interface, succede che tutto in Java interfacesia anche la sua interfaccia pubblica (concetto OO).

Tutti! Scrivi la seconda e la terza frase della risposta. Laminalo. Mettilo nel tuo portafoglio. Riferimento spesso.
radarbob,

5

Può essere molto allettante per i nuovi sviluppatori pensare alle interfacce come un inconveniente che aggiungi semplicemente perché ti viene detto che è "best practice". Se lo stai facendo ciecamente, sa di programmazione di culto del carico .

Ci sono una serie di ottime ragioni per cui dovresti prendere in considerazione le interfacce per sviluppi più grandi piuttosto che mettere insieme un insieme di classi.

Quadri beffardi

Se vuoi testare un'applicazione multi-layer senza dover creare oggetti per i vari layer, senza dubbio vorrai usare i framework beffardi per deridere i layer. Le interfacce sono ampiamente utilizzate qui.

Iniezione di dipendenza

Mentre sono in qualche modo sfuggiti al favore a causa del gonfiore che aggiungono, l'idea è solida - la capacità di scambiare semplicemente concrezioni basate su un'interfaccia. Il principio può essere trovato anche in molti modelli di progettazione come il modello di fabbrica.


Questi approcci sono sintetizzati nella D dai principi SOLID . Il punto è questo: usa le astrazioni, non le concrezioni.

Come i modelli di progettazione stessi, quando usarli è un appello di giudizio. Il punto da asporto è capire come le interfacce sono utili non solo incollarle alle tue classi perché senti di doverlo fare. C'è una buona discussione sui vari meriti di DIP e YAGNI qui .


Si noti che l'iniezione di dipendenza e i contenitori IoC sono due cose completamente diverse (uno è un principio di progettazione e l'altro è un modo per mettere concretamente in pratica quel principio usando vari framework). È possibile utilizzare DI senza utilizzare un contenitore IoC.
Sara,

@kai "Puoi usare DI senza usare un contenitore IoC" . Penso che i negozi di sviluppo più maturi possano (e facciano). Semplicemente non è necessario inquinare il codice per quello che è apparentemente un semplice concetto. Joel ha una visione simile.
Robbie Dee,

YAGNI è molto complicato. Qualunque sia la filosofia, nella maggior parte dei casi, nella realtà, YAGNI è usato come scusa per tagliare gli angoli. Evitare l'accoppiamento dovrebbe essere preso più seriamente. È frustrante vedere in molte riunioni di mischia, uno sviluppatore si definisce bloccato perché qualcun altro non ha finito il proprio lavoro. Come mai bloccare un altro sviluppatore sta facendo risparmiare tempo a qualcuno? Il mio consiglio è di utilizzare un'interfaccia per evitare l'accoppiamento. Naturalmente, non è necessario utilizzare un'interfaccia per le classi Model.
Ripal Barot,
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.