Devo scrivere un'API di interfaccia prima di un'implementazione?


14

Recentemente ho approfondito la programmazione più "organizzata" e ho imparato che avrei dovuto programmare su un'interfaccia, non su un'implementazione. Con questo in mente, sarebbe meglio "disegnare" un progetto in interfacce prima di scrivere l'implementazione per esso dove possibile?

E se questo è il caso, nel caso di utilizzo di librerie di terze parti (ad esempio Lidgren), dovrei anche avvolgere quelle nelle interfacce e risolverle attraverso i contenitori IOC, o è giusto esporle alle interfacce?


Nella mia esperienza personale - è bello progettare prima l'architettura - la responsabilità di ogni classe. Non devi scriverlo, basta pensarci o disegnarlo su un foglio. Quindi si tratta di preferenze personali, ma consiglio di scrivere prima i commenti sui documenti per ogni metodo che inizi a implementare. Scrivere documenti ti fa davvero pensare alla funzionalità prima di iniziare a scrivere codice.
Sulthan,

Sì, e programma sulle interfacce (o classi astratte per quella materia) prima di implementarle. Aiuta a ottenere il flusso di messaggi dal client al server e viceversa "giusto" prima di essere impantanato (e investito) nelle implementazioni. Ottima presentazione in materia: come progettare una buona API e perché è importante
Marjan Venema,

Risposte:


8

Sfortunatamente, questo spesso si riduce alle preferenze personali.

Ciò che hai descritto finora, tuttavia, sembra buono. In effetti, se volessi (e lo consiglio) potresti usare il seguente approccio:

  1. Scrivi lo scheletro dell'applicazione come interfacce, classi astratte (stubbed) e classi (anche stub)
  2. Scrivi i tuoi test su quelle interfacce e stub (falliranno per ora)
  3. Scrivi le tue implementazioni (i test inizieranno a passare al termine dell'implementazione)

Ti stai concentrando sul tentativo di scrivere più codice "organizzato". Seguire TDD ti aiuterà in questo.

Alcuni punti extra:

  • I contenitori IoC sono convenienti. Usali e DI il più possibile.
  • Non avvolgere le biblioteche 3rd party. Questo allenterà l'accoppiamento tra il tuo codice (codice che controlli) e il codice di terze parti (codice che non controlli)

1
Questo era quello che avevo inizialmente pensato ma mi è stato detto che avrebbe violato il principio YAGNI. Il problema che riscontro con molti dei miei progetti che non finiscono mai è che diventano rapidamente non mantenibili con la quantità di codice BLOB che ho scritto, perché non lo organizzo correttamente o pianifico il mio piano di attacco.
Dan Pantry,

quale parte violerebbe YAGNI?
MetaFight,

Confezione di librerie di terze parti.
Dan Pantry,

2
Immagino che si riduce a: Quali sono le probabilità che la biblioteca di terze parti cambi? Se esiste una probabilità dello 0%, assicurati di YAGNI. Ma raramente è così. Inoltre, il wrapping delle librerie di terze parti può rendere più semplice il test del tuo altro codice (se, ad esempio, non riesci a deridere la libreria di terze parti)
MetaFight,

1
@DanPantry: il wrapping delle librerie di terze parti non è una violazione di YAGNI, ma una protezione molto necessaria contro "un'infestazione da parte di biblioteche di terze parti del tuo codice". Non si tratta solo di essere in grado di scambiare una libreria, ma come MetaFight afferma anche una difesa contro i cambiamenti nelle versioni più recenti della libreria che altrimenti richiederebbero cambiamenti in tutto il proprio codice. Avvolgendo la libreria (e soprattutto i suoi tipi specifici: classi, enum, strutture ecc.), Si isola il proprio codice e si ha un unico punto da cambiare quando la libreria cambia (per qualsiasi motivo).
Marjan Venema,

13

Sì, dovresti programmare contro interfacce piuttosto che implementazioni conosciute, e sì, dovresti prima costruire interfacce piuttosto che farle emergere dal tuo codice.

Le ragioni di entrambe le raccomandazioni sono sostanzialmente le stesse: la programmazione informatica riguarda in gran parte i fattori umani. Molti lo trovano sorprendente, ma considerano: esiste un numero pressoché infinito di modi diversi per risolvere lo stesso problema di elaborazione che funziona ugualmente bene. Quasi tutti sono completamente impossibili da dare un senso a chiunque non li abbia scritti (o in effetti all'autore poco tempo dopo).

Ne consegue che una buona ingegneria del software riguarda in gran parte il modo di ottenere l'effetto desiderato (calcolo corretto con ragionevole efficienza) in un modo che consenta di lavorare in seguito sul codice sorgente. Le interfacce e le API sono una parte cruciale di quella disciplina: ti permettono di pensare a un problema ad un livello di descrizione alla volta. Ciò è molto più semplice che pensare contemporaneamente alle regole di coerenza aziendale e alle implementazioni di elenchi collegati, pertanto è meglio imporre forzatamente una tale separazione delle preoccupazioni che consentire al programmatore del client di usare il codice nel modo che preferisce.

Questo è difficile da credere per molti programmatori di cowboy, che sono convinti di comprendere tutto ciò che scrivono, sono molto meglio dei pensatori medi e possono gestire tutta la complessità che dà problemi ai programmatori "minori". Non essere consapevoli dei propri limiti cognitivi è un fenomeno estremamente comune - ecco perché le migliori pratiche nell'organizzazione del codice sono così estremamente importanti (e così spesso ignorate).

Per ripetere, le interfacce e le barriere API sono in gran parte buone , anche quando si collabora solo con se stessi. Per quanto riguarda le librerie esterne, se portano con sé un'API ben congegnata, non vedo alcun problema nell'usarla in quanto è purché non preveda di dover sostituire quella libreria con un'altra. Altrimenti, un wrapper o un livello anticorruzione può essere un'ottima idea.


Mi piace il tuo punto su SE in gran parte sul raggiungimento dell'effetto desiderato in un modo che consenta di lavorare in seguito con il codice sorgente. Vorrei essere stato in grado di esprimerlo così bene nel mio ultimo lavoro, dove ho sempre lottato per un codice pulito!
MetaFight,

Esiste una convenzione di denominazione per le API che sono solo interfacce che finirò per usare ovunque? Ad esempio, se sto eseguendo un modello di comando, lo chiamo "comandabili"?
Snoop,

@StevieV Ce ne sono vari, ad esempio IBlahimplementati da Blaho Blahimplementati da BlahImpl. Non mi piacciono entrambi e tendo ad usare Blahimplementato da OralBlah, WrittenBlaho ASLBlah. Ma come al solito, è più importante conformarsi alla base di codice e alle aspettative esistenti che a qualsiasi standard generale.
Kilian Foth,

4

Piuttosto che programmare semplicemente per interfacce, perché non esaminare Test Driven Development / Design (TDD)?

Molte persone pensano che TDD sia una pratica di test, ma in realtà è un approccio progettuale in cui si lascia che i test espongano come il codice verrà utilizzato tramite test (inizialmente tramite test unitari, ma può anche essere tramite test di integrazione).

La programmazione alle interfacce è un'arma importante nel tuo set di strumenti, ma come la maggior parte delle cose, non è sempre la soluzione / tecnica / pratica appropriata, poiché non è sempre necessaria. È necessario programmare su interfacce dove è necessario.

L'uso di TDD ti costringerà ad esplorare dove tali interfacce sono importanti e dove, francamente, non importa. E alla fine dovresti avere un set abbastanza buono di unit test sulla tua base di codice.

Per quanto riguarda l'uso di librerie di terze parti, consiglio vivamente di includerle nelle proprie astrazioni, ove appropriato; e non lasciare che i clienti della tua API "sappiano" di loro.

In bocca al lupo!

[modifica: ho visto la risposta di megaflight - sono completamente d'accordo]


2
TDD ti ha implicitamente pensato in termini di interfaccia piuttosto che di implmenetazione, anche se potrebbe non essere una dichiarazione formale di "interfaccia".
DougM,

1
Questa è un'ottima risposta +1 per suggerire TDD, che penso sia la soluzione al vero problema del PO di dove iniziare quando si lavora su un nuovo progetto, e farei +1 di nuovo se potessi per "L'uso di TDD ti costringerebbe a esplorare dove tali interfacce sono importanti e dove, francamente, non importa. "
Benjamin Hodgson,

2

Penso che sia eccessivo. Se l'utente della tua API non ha bisogno di essere costretto a implementare / utilizzare qualcosa in un certo modo, lo lascerei fuori. Le interfacce sono contratti, se non ne ho bisogno, perché darmene uno?

Penso che le persone abusino delle interfacce. Stai aggiungendo un livello di complessità che non è necessario nella maggior parte dei casi.


Penso che le persone sottoutilizzino le interfacce. Se vuoi creare pezzi riutilizzabili, l'interfaccia non è solo un'aggiunta "piacevole da avere", ma la cosa principale di cui occuparti. Oltre all'attuazione effettiva ovviamente.
JensG,

1

Programmare contro un contratto è quasi sempre una buona idea. Quel contratto non deve necessariamente essere un'interfaccia, ma può essere adempiuto da una classe. A mio avviso, le interfacce sono state in qualche modo abusate insieme a DI a causa di problemi di test unitari e framework di simulazione.

Personalmente preferisco introdurre interfacce solo quando molto probabilmente avrei potuto o avere più di 1 implementazione di un contratto. Le interfacce sono ottime per i repository in cui desidero sottrarre l'accesso ai dati, ma probabilmente meno per la mia logica aziendale standard che è probabilmente relativamente poco flessibile.

Ora non avere un'interfaccia può causare problemi con i test unitari, specialmente per i puristi. Ma sono interessato a deridere le dipendenze esterne dei miei programmi, non le sue dipendenze interne. Voglio che i miei test eseguano la convalida del codice, non facendo eco alla struttura del codice.

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.