Design pattern per la creazione di oggetti nascosti dal client

2

Ho difficoltà a capire quale modello di progettazione sarebbe il migliore per il seguente scenario. Un'applicazione utilizza un database. Quel database può essere la versione di produzione ( SQL ) o una versione di prova ( Test ). Ecco il mio design attuale:

Il problema con questo design è che l'applicazione deve creare Test , cosa che non dovrebbe essere eseguita (poiché è puramente per il test). Tuttavia, L'applicazione deve in qualche modo ottenere un riferimento al database che utilizzerà, SQL o Test .

Quale modello di progettazione posso implementare per ingannare l'applicazione nella creazione della versione Test del database? Come apparirebbe questa soluzione in UML?

Ho già fatto abbastanza ricerche per restringere le mie scelte a Fabbrica astratta, Builder o Metodo di fabbrica (o la combinazione di qualsiasi)

    
posta Corey 17.11.2018 - 04:39
fonte

5 risposte

4

Quando vuoi che una parte del tuo programma istanzia una determinata sottoclasse, ma nascondi il comportamento di istanza da un altro codice client (la tua classe Application ), dovresti probabilmente andare con il metodo di produzione DP.

Questo si riferisce alla parte di istanziazione. Per quanto riguarda le effettive differenze comportamentali tra le sottoclassi, la strategia DP può essere una buona soluzione, come affermato da @Paul.

    
risposta data 17.11.2018 - 09:43
fonte
4

Prima di entrare nel merito della tua domanda, ti faccio notare che la struttura che hai fornito ha alcuni problemi (che hai notato, ma lascia che me ne esamini un po '):

Questoprogettoutilizzainversionedidipendenzadirendereilcliente(Application)eunservizioconcreto(ungatewayDB-SQL,Test)indipendentil'unodall'altro,rendendoognidipendonosull'interfacciaIDatabase.Tuttavia,ilfattochel'applicazionecreiilservizioconcretolorendedipendentedaltipoconcreto,cheinunacertamisuracompromettel'interaconfigurazione.Lamisuraincuiquestoèinrealtàunproblemadipendepensacome:ciòcheilcodicedelclientfa,ladisciplinadeglisviluppatori(interminidiisolarel'accoppiamentoaunpezzobendefinitodellaclasseApplication),letecnicheutilizzate(adesempio,ilquadrocambiaunpo'seilservizioconcretovieneistanziatoattraversolariflessione),comesidesideraorganizzareilcodiceinlibrerie,ecc.

BasandosisolosulletecnicheOOtradizionali,ilmodoperassicurarsicheilcodiceclientenonèaconoscenzadeitipiconcreticheimplementanoIDatabaseèquellodiintrodurreunterzopezzodicodicecheconoscetuttiquestitipi,edèquindiingradodiagganciarli(equesto,asuavolta,consenteaquestitipidirimaneredisaccoppiati).Negliapproccicheutilizzanol'iniezionedidipendenza,questaterzapartedicodicesaràingenerelaradicedicomposizione(piùomeno,ilpuntodiingressodell'applicazione).

UnmodopercambiareildiagrammadiclasseoriginalechehaipostatoinmodocheseguaquestastrutturaèdividerelaclasseApplicationindueparti.Unapartesaràlaradicedellacomposizione(iltingchecreaesicollegaegiral'altraparteoabilitaaltrimentil'esecuzione).L'altrapartesaràilcodiceclientcheutilizzaIDatabase.

Ciòconsenteditestarequest'altraparte(lalogicaaziendaleeffettivacontenutanelcodiceclient)inisolamento(adesempio,testdell'unità)fornendoun'istanzadidatabaseconcretanelcodiceditesttramitel'iniezionedelcostruttore.

Ilmodochehodescrittofinora,quandositrattadicrearecasiconcreti,laradicecomposizioneutilizzaessenzialmente"codice come configurazione", ma se si vuole (o la necessità di), si può rendere veramente configurabile da averlo leggi un file di configurazione e prendi le sue decisioni basandosi su quei dati, oppure puoi contare su un contenitore DI per gestire la configurazione, l'iniezione delle dipendenze e il ciclo di vita dell'oggetto per te.

    
risposta data 17.11.2018 - 16:17
fonte
3

Quindi, la risposta breve alla tua domanda è che il modello che stai cercando di trovare è probabilmente il Pattern di strategia , poiché sei interessato a selezionare un'implementazione in fase di runtime. Soprattutto se l'implementazione nel tuo codice è molto diversa (ad esempio il tuo vero DB è un RDBMS ma il tuo DB di test è solo file flat di dati di test)

Ma, detto questo, penso sia un errore cercare di pensare al tuo software in termini di quali sono i modelli di progettazione di cui ha bisogno. Per la maggior parte, dovresti iniziare con l'approccio più semplice possibile e rifattorarti su un modello quando il tuo approccio semplice non è abbastanza buono.

Che cosa intendo?

Bene, prendiamo il tuo caso. Nella versione più semplice, direi che tutto ciò che devi fare è cambiare la connessione del DB in base all'ambiente (prod o test). Quindi, inserisci le informazioni di connessione in una variabile d'ambiente e fallo con esso.

Se ritieni di aver bisogno di più modifiche, supponiamo che tu abbia bisogno di dati seme diversi. Quindi, aggiungi una funzione al test che viene eseguita prima della logica di test per aggiornare il contenuto o forse qui hai un file di dati seed selezionato in base a un altro env var.

Se hai bisogno di ulteriori differenze, ci sono buone probabilità che il tuo design sia negativo, perché dovresti spendere così tanto tempo per testare cose diverse da quelle che stai facendo in produzione?

Ok, ti ho ingannato. In realtà c'è davvero poca ragione per usare mai il modello di strategia per risolvere questo problema. Ma è quello che stavi "cercando" con il modo in cui hai posto la domanda. Vedi cosa intendo?

    
risposta data 17.11.2018 - 09:05
fonte
3

Nel tuo progetto, l'applicazione crea il suo database. Tuttavia, l'applicazione non ha le informazioni necessarie per scegliere il database. Invece, passa un database all'applicazione.

Ad esempio, nel codice di avvio dell'applicazione:

Database db = new MySQLConnection(...);
Application app = new Application(db);
app.run();

Ma nel tuo codice di test:

Database db = new TestDBConnection(...);
Application app = new Application(db);
app.run();

Cioè, iniettiamo la dipendenza nell'applicazione, usando l'interfaccia Database .

Invece di iniettare manualmente le dipendenze attraverso gli argomenti del costruttore o gli argomenti delle funzioni, potresti anche usare un framework di iniezione delle dipendenze. Quindi, l'applicazione richiederebbe un oggetto Database dal framework DI e avremmo configurato in precedenza il framework DI per utilizzare una classe specifica per implementare l'interfaccia Database . Tuttavia, l'uso di un framework non è richiesto e l'iniezione del costruttore funziona bene per la maggior parte dei progetti.

    
risposta data 17.11.2018 - 15:40
fonte
1

Ecco l'UML per completare le altre risposte:

Fabbrica

Esistono almeno tre varianti del modello Factory, ad esempio, Statico, Calcestruzzo, Metodo di fabbrica e Fabbrica astratta. Static non funzionerà davvero con un'interfaccia, quindi Concrete sarebbe adeguata se tutto ciò che si vuole fare non è che Application conosca i dettagli della creazione. Il metodo di fabbrica e la fabbrica astratta non sembrano essere appropriati per la situazione che hai indicato nella domanda.

Quando l'applicazione utilizza una fabbrica, l'applicazione decide quale database viene istanziato, anche se i dettagli di come il database è istanziato sono nascosti in fabbrica.

Iniezionedidipendenza

L'applicazionenondecidequaledatabasevieneistanziato;qualchealtraclasse,adesempioDecider,lofa.Deciderpuòusareunafabbricaperisolarladaidettaglidiistanziazione(comesopra).

    
risposta data 25.11.2018 - 23:33
fonte