Capire "programmare su un'interfaccia"

21

Mi sono imbattuto nel termine "programmare un'interfaccia invece di un'implementazione" e credo di capire che cosa significa. Ma voglio essere sicuro di capire i benefici e le possibili implementazioni.

"Programmare su un'interfaccia" significa che, quando possibile, si dovrebbe fare riferimento a un livello più astratto di una classe (un'interfaccia, una classe astratta, o qualche volta una superclasse di qualche tipo), invece di riferirsi a un'implementazione concreta.

Un esempio comune in Java, è quello di utilizzare:

List myList = new ArrayList(); anziché ArrayList myList = new ArrayList(); .

Ho due domande a riguardo:

  1. Voglio assicurarmi di comprendere i principali vantaggi di questo approccio. Penso che i benefici siano soprattutto la flessibilità. Dichiarare un oggetto come un riferimento di più alto livello, piuttosto che un'implementazione concreta, consente maggiore flessibilità e mantenimento in tutto il ciclo di sviluppo e in tutto il codice. È corretto? La flessibilità è il vantaggio principale?

  2. Esistono altri modi per "programmare su un'interfaccia"? Oppure "dichiarare una variabile come interfaccia piuttosto che come implementazione concreta" è l'unica implementazione di questo concetto?

Sono non parlando dell'interfaccia del costrutto Java . Sto parlando del principio OO "programmare un'interfaccia, non un'implementazione". In questo principio, l'interfaccia "mondo" si riferisce a qualsiasi "supertipo" di una classe - un'interfaccia, una classe astratta o una semplice superclasse che sia più astratta e meno concreta delle sottoclassi più concrete.

    
posta Aviv Cohn 14.03.2014 - 12:41
fonte

6 risposte

34

"Programming to an interface" means, that when possible, one should refer to a more abstract level of a class (an interface, abstract class, or sometimes a superclass of some sort), instead of refering to a concrete implementation.

Questo non è corretto . O almeno, non è del tutto corretto.

Il punto più importante viene da una prospettiva di progettazione del programma. Qui, "programmare un'interfaccia" significa focalizzare il tuo design su cosa sta facendo il codice, non come lo fa. Questa è una distinzione fondamentale che spinge il tuo design verso la correttezza e la flessibilità.

L'idea principale è che i domini cambino molto più lentamente del software. Di 'che hai un software per tenere traccia della tua lista della spesa. Negli anni '80, questo software avrebbe funzionato contro una riga di comando e alcuni file flat su floppy disk. Allora hai un'interfaccia utente. Quindi potresti inserire la lista nel database. Successivamente potrebbe essere spostato sul cloud o sui telefoni cellulari o sull'integrazione con Facebook.

Se hai progettato il tuo codice appositamente per l'implementazione (floppy disk e linee di comando) non sarai pronto per le modifiche. Se hai progettato il tuo codice nell'interfaccia (manipolando una lista della spesa), l'implementazione è libera di cambiare.

    
risposta data 14.03.2014 - 13:56
fonte
15

La mia comprensione della "programmazione in un'interfaccia" è diversa da quella suggerita dalla domanda o dalle altre risposte. Il che non vuol dire che la mia comprensione sia corretta, o che le cose nelle altre risposte non siano buone idee, solo che non sono quello che penso quando sento quel termine.

Programmare su un'interfaccia significa che quando ti viene presentata un'interfaccia di programmazione (che si tratti di una libreria di classi, un insieme di funzioni, un protocollo di rete o qualsiasi altra cosa) continui a utilizzare solo le cose garantite dall'interfaccia. Potresti avere conoscenza dell'implementazione sottostante (potresti averlo scritto), ma non dovresti mai usare quella conoscenza.

Ad esempio, supponiamo che l'API ti presenti con un valore opaco che è un "handle" per qualcosa di interno. La tua conoscenza potrebbe dirti che questo handle è in realtà un puntatore e potresti dereferenziarlo e accedere ad alcuni valori, il che potrebbe consentire di eseguire facilmente alcune attività che desideri eseguire. Ma l'interfaccia non ti fornisce questa opzione; è la tua conoscenza della particolare implementazione che fa.

Il problema con questo è che crea un strong accoppiamento tra il codice e l'implementazione, esattamente ciò che l'interfaccia doveva impedire. A seconda della politica, potrebbe significare che l'implementazione non può più essere modificata, perché ciò potrebbe inficiare il codice o che il codice sia molto fragile e continui a infrangere ogni aggiornamento o modifica dell'implementazione sottostante.

Un grande esempio di questo è programmi scritti per Windows. Il WinAPI è un'interfaccia, ma molte persone hanno usato trucchi che funzionavano a causa della particolare implementazione, ad esempio Windows 95. Questi trucchi forse rendevano i loro programmi più veloci, o permettevano loro di fare cose in meno codice di quanto sarebbe stato altrimenti necessario. Ma questi trucchi significavano anche che il programma si arrestava in modo anomalo su Windows 2000, perché l'API era implementata in modo diverso lì. Se il programma fosse abbastanza importante, Microsoft potrebbe effettivamente andare avanti e aggiungere qualche modifica alla loro implementazione in modo che il programma continui a funzionare, ma il costo di ciò è la maggiore complessità (con tutti i problemi che ne derivano) del codice di Windows. Rende la vita ancora più dura per le persone Wine, perché cercano di implementare anche WinAPI, ma possono solo fare riferimento alla documentazione su come farlo, il che porta a molti programmi che non funzionano come dovrebbero perché (accidentalmente o intenzionalmente) si basano su alcuni dettagli di implementazione.

    
risposta data 14.03.2014 - 15:03
fonte
7

Posso solo parlare della mia esperienza personale, poiché non mi è mai stato insegnato neanche formalmente.

Il tuo primo punto è corretto. La flessibilità ottenuta deriva dal non essere in grado di richiamare accidentalmente i dettagli di implementazione della classe concreta in cui non dovrebbero essere invocati.

Ad esempio, considera un'interfaccia ILogger che è attualmente implementata come una classe di LogToEmailLogger concreta. La classe LogToEmailLogger espone tutti i metodi e le proprietà di ILogger , ma ha anche una proprietà specifica dell'implementazione sysAdminEmail .

Quando il tuo logger viene utilizzato nella tua applicazione, non dovrebbe preoccuparsi del codice che consuma impostare il sysAdminEmail . Questa proprietà deve essere impostata durante la configurazione del logger e deve essere nascosta al mondo.

Se si stava codificando contro l'implementazione concreta, si potrebbe accidentalmente impostare la proprietà dell'implementazione quando si utilizza il logger. Ora, il tuo codice applicazione è strettamente collegato al tuo registratore, e il passaggio ad un altro logger richiede prima il disaccoppiamento del tuo codice da quello originale.

In questo senso, codifica su un'interfaccia allenta l'accoppiamento .

Riguardo al secondo punto: un'altra ragione che ho visto per la codifica di un'interfaccia è la riduzione della complessità del codice.

Ad esempio, immagina di avere un gioco con le seguenti interfacce I2DRenderable , I3DRenderable , IUpdateable . Non è raro che un singolo componente di gioco abbia contenuti sia 2D che 3D. Altri componenti possono essere solo 2D e altri solo 3D.

Se il rendering 2D viene eseguito da un modulo, allora ha senso mantenere una raccolta di I2DRenderable s. Non importa se gli oggetti nella sua collezione sono anche I3DRenderable o IUpdateble poiché gli altri moduli saranno responsabili del trattamento di quegli aspetti degli oggetti.

La memorizzazione degli oggetti renderizzabili come un elenco di I2DRenderable mantiene bassa la complessità della classe di rendering. La logica di rendering 3D e di aggiornamento non è di suo interesse e quindi quegli aspetti dei suoi oggetti figli possono e devono essere ignorati.

In questo senso, la codifica su un'interfaccia mantiene la complessità bassa da problemi di isolamento .

    
risposta data 14.03.2014 - 13:00
fonte
3

Un'analogia con il mondo reale potrebbe aiutare:

Una presa di corrente elettrica in un'interfaccia.
Sì; quella cosa a tre punte all'estremità del cavo di alimentazione dalla TV, dalla radio, dall'aspirapolvere, dalla lavatrice, ecc.

Qualsiasi appliance che abbia una spina principale (cioè implementa l'interfaccia "ha un'interfaccia di rete") può essere trattata in esattamente nello stesso modo; possono essere tutti collegati a una presa a muro e possono trarre energia da quella presa.

Ciò che ogni singola appliance fa è completamente diversa. Non stai andando molto lontano a pulire i tuoi tappeti con la TV e la maggior parte delle persone non guarda la loro lavatrice per l'intrattenimento. Ma tutti questi apparecchi condividono il comportamento comune di poter essere inseriti in una presa a muro.

Questo è ciò che ti danno le interfacce.
Unificato comportamenti che possono essere eseguiti da molte diverse classi di oggetti, senza la necessità delle complicazioni dell'ereditarietà.

    
risposta data 05.05.2016 - 13:26
fonte
2

Ci sono forse due usi dell'interfaccia di parole usati qui. L'interfaccia a cui ti stai principalmente riferendo nella tua domanda è un'interfaccia Java . Questo è specificamente un concetto di Java, più in generale è un'interfaccia di linguaggio di programmazione.

Direi che programmare su un'interfaccia è un concetto più ampio. Le ormai popolari API REST disponibili per molti siti Web sono un altro esempio del concetto più ampio di programmazione di un'interfaccia a un livello superiore. Creando un livello tra il funzionamento interno del codice e il mondo esterno (persone su Internet, altri programmi, persino altre parti dello stesso programma) puoi modificare qualsiasi cosa all'interno del tuo codice finché non cambi il mondo esterno è in attesa, dove è definito da un'interfaccia, o un contratto che intendi onorare.

Questo ti fornisce la flessibilità di refactoring del codice interno senza dover dire tutte le altre cose che dipendono dalla sua interfaccia.

Significa anche che il tuo codice dovrebbe essere più stabile. Attaccando all'interfaccia quindi non si dovrebbe rompere il codice di altre persone. Quando è necessario modificare l'interfaccia, è possibile rilasciare una nuova versione principale (1.a.b.c in 2.x.y.z) dell'API che segnala la presenza di modifiche all'interfaccia della nuova versione.

Come @Doval fa notare nei commenti su questa risposta, c'è anche la localizzazione degli errori. Questi penso che tutti si riducano all'incapsulamento. Proprio come lo useresti per oggetti in un design orientato agli oggetti, quindi questo concetto è utile anche a un livello più alto.

    
risposta data 14.03.2014 - 12:58
fonte
0

Il termine "programmare su un'interfaccia" è aperto a molte interpretazioni. L'interfaccia nello sviluppo del software è una parola molto comune. Ecco come spiegherò il concetto agli sviluppatori junior che ho formato nel corso degli anni.

Nell'architettura software ci sono una vasta gamma di confini naturali. Esempi comuni includono

  • Il limite di rete tra i processi client e server
  • Il limite dell'API tra un'applicazione e una libreria di terze parti
  • Limite del codice interno tra diversi domini aziendali all'interno del programma

Ciò che conta, è che quando esistono questi confini naturali, vengono identificati e viene specificato il contratto di come si comporta quel confine. Metti alla prova il tuo software non se l'"altro lato" si comporta, ma se le tue interazioni corrispondono alle specifiche.

Le conseguenze di questo sono:

  • I componenti esterni possono essere scambiati fintanto che implementano le specifiche
  • Punto naturale per i test unitari per convalidare il comportamento corretto
  • I test di integrazione diventano importanti - le specifiche erano ambigue?
  • Come sviluppatore hai un piccolo mondo di preoccupazione quando lavori su una particolare attività

Sebbene gran parte di questo possa riguardare classi e interfacce, è altrettanto importante rendersi conto che si riferisce anche a modelli di dati, protocolli di rete e in un modo più generale di lavorare con più sviluppatori

    
risposta data 05.05.2016 - 14:18
fonte

Leggi altre domande sui tag