Perché un programmatore desidera separare l'implementazione dall'interfaccia?

18

Il modello di progettazione del ponte separa l'implementazione dall'interfaccia di un programma.

Perché questo è vantaggioso?

    
posta David Faux 30.03.2012 - 04:30
fonte

3 risposte

34

Permette di cambiare l'implementazione indipendentemente dall'interfaccia. Questo aiuta ad affrontare i mutevoli requisiti.

L'esempio classico sta sostituendo l'implementazione dello storage con un'interfaccia con qualcosa di più grande, migliore, più veloce, più piccolo o comunque diverso senza dover cambiare il resto del sistema.

    
risposta data 30.03.2012 - 04:32
fonte
22

Oltre alla risposta di Daniel, separare l'interfaccia dall'implementazione attraverso concetti come polymorphism ti permette di creare diverse implementazioni della stessa interfaccia che fanno cose simili in modi diversi.

Ad esempio, molte lingue hanno un concetto di stream da qualche parte nella libreria standard. Un flusso è qualcosa che contiene dati per l'accesso seriale. Ha due operazioni di base: Leggi (carica il prossimo numero X di byte dallo stream) e Scrivi (aggiungi il numero X di byte di dati allo stream), e talvolta un terzo, Cerca (ripristina la "posizione corrente" dello stream) in una nuova posizione).

È un concetto abbastanza semplice, ma pensa a tutte le cose che potresti fare con esso. La più ovvia è quella di interagire con i file sul disco. Un flusso di file ti consente di leggere i dati da un file o di scrivere su di esso. E se invece volessi inviare dati tramite una connessione di rete?

Se ti affidassi direttamente alle implementazioni, dovresti scrivere due routine completamente diverse per salvare gli stessi dati in un file o inviarlo tramite la rete. Ma se si dispone di un'interfaccia di flusso, è possibile creare due diverse implementazioni ( FileStream e NetworkStream ) che incapsulano i dettagli specifici di invio dei dati dove deve andare, e quindi è sufficiente scrivere il codice che si occupa di salvare un file una volta. All'improvviso le tue routine SaveToFile e SendOverNetwork sono molto più semplici: impostano semplicemente un flusso del tipo appropriato e lo passano alla routine SaveData , che accetta un'interfaccia di flusso - non ha bisogno di preoccuparsi di cosa digita, purché possa eseguire l'operazione Scrittura - e salva i dati nello stream.

Questo significa anche che se il tuo formato di dati cambia, non devi cambiarlo in più posti diversi. Se centralizzi il tuo codice di salvataggio dei dati in una routine che richiede uno stream, allora questo è l'unico posto che deve essere aggiornato, quindi non puoi introdurre accidentalmente un bug cambiando solo una posizione quando è necessario cambiarle entrambe. Quindi separare le interfacce dalle implementazioni e utilizzare il polimorfismo rende il codice più semplice da leggere e capire, ed è meno probabile che abbia dei bug.

    
risposta data 30.03.2012 - 05:36
fonte
2

Hai davvero due domande molto diverse qui, anche se sono correlate.

La domanda più generale è quella nel titolo, perché dovresti separare l'interfaccia dall'implementazione in generale. La seconda domanda è perché il modello del ponte è utile. Sono correlati perché il pattern bridge è un modo specifico di separare l'interfaccia dall'implementazione, che ha anche altre conseguenze specifiche.

La domanda generale è qualcosa di vitale da comprendere per ogni programmatore. È ciò che impedisce alle modifiche in un programma di diffondersi ovunque. Non riesco a immaginare che gli esseri umani possano programmare senza usare questo.

Quando scrivi una semplice istruzione addizionata in un linguaggio di programmazione, questa è già un'astrazione, (anche se non usa l'overloading dell'operatore per aggiungere matrici o qualcosa di simile) che passa attraverso un bel po 'di altro codice prima di esso finalmente viene eseguito su un circuito nel tuo computer. Se non ci fosse alcuna separazione dell'interfaccia (diciamo "3 + 5"), dall'implementazione (un mucchio di codice macchina), allora dovresti cambiare il tuo codice ogni volta che l'implementazione cambia (come volevi correre su un nuovo processore).

Anche all'interno di una semplice applicazione CRUD, ogni firma di metodo è, in senso lato, l'interfaccia per la sua implementazione.

Tutti questi tipi di astrazione hanno lo stesso obiettivo di base: il codice chiamante esprime il proprio intento nel modo più astratto possibile che dia all'installer tutte le informazioni necessarie. Ciò fornisce il minimo accoppiamento possibile tra loro e limita l'effetto a catena quando il codice deve essere modificato il più possibile.

Sembra semplice, ma in pratica diventa complicato.

Il pattern bridge è un modo specifico per separare determinati bit di implementazione in interfacce. Il diagramma di classe del modello è più informativo della descrizione. È più un modo per avere moduli collegabili che un bridge, ma lo hanno chiamato bridge perché viene spesso utilizzato dove i moduli sono stati creati prima dell'interfaccia. Pertanto, creando un'interfaccia comune per implementazioni esistenti simili, si "fa da ponte" alla differenza e si consente al codice di funzionare con qualsiasi implementazione.

Quindi, diciamo che volevi scrivere un add-on in un word processor, ma vuoi che funzioni su più word processor. Potresti creare un'interfaccia che estrae la funzionalità basata sul word processor che ti serve (e che deve essere implementabile da ogni word processor poiché non puoi cambiarle), e un implementatore di quell'interfaccia per ogni word processor che vuoi supportare. Quindi la tua applicazione può chiamare quell'interfaccia e non preoccuparti dei dettagli di ogni word processor.

In realtà è leggermente più dettagliato di quello, perché ogni classe potrebbe essere realmente una gerarchia di classi (quindi, potrebbe non esserci un word processor astratto, ma un documento astratto, TextSelection astratto, ecc., con implementazioni concrete per ciascuno) , ma è la stessa idea.

È un po 'come una facciata, tranne che in questo caso il livello di astrazione si concentra sulla fornitura della stessa interfaccia a più sistemi sottostanti.

È collegato a Inversion Of Control, poiché l'implementatore concreto verrà passato a metodi o costruttori e determinerà l'effettiva implementazione chiamata.

    
risposta data 30.03.2012 - 20:06
fonte

Leggi altre domande sui tag