Pattern degli oggetti auto-inizializzabili

3

Sto lavorando su un sistema in cui applichiamo coerentemente "schemi" o approcci come "Dependency Injection".

Preferirei non esporre l'attuale tecnologia di base per non ottenere soluzioni prevenute, perché penso che la mia domanda si applicherebbe a molte lingue e runtime diversi. È sufficiente dire che sto usando una piattaforma Object Oriented , dove ho lezioni, delegati e tutte le shenanigans attese in uno spazio linguistico moderno.

Durante lo sviluppo, ho seguito il seguente schema:

  • Ho una classe chiamata ConfigurationProvider che ha un metodo semplice get in cui puoi passargli sectionName s, con cui puoi recuperare la configurazione in un modo molto disaccoppiato. La configurazione restituita da get è in realtà una struttura hash, modulo che il chiamante può quindi trasferire le chiavi di configurazione e recuperare i valori di configurazione.

  • La maggior parte di alto livello services che mi aspetto di ricevere un'istanza di questo oggetto per poter accedere alla configurazione. In questo modo, sono in grado di configurarsi. Questo mi dà una certa flessibilità per spostare la configurazione (la struttura finale fornita a ciascun oggetto deve, ovviamente, essere rispettata) senza influenzare l'oggetto chiamante.

Facciamo un esempio:

ConfigurationProvider config = new ConfigurationProvider();
Cache cache = new Cache(config);
UserProfileService userProfileSvc = new UserProfileService(cache);

Come puoi vedere, tutti i dettagli della cache come host e porta, time-to-live predefinito ecc. sono completamente astratti. Gli utenti devono quindi seguire le regole di configurazione della cache. Ad esempio, la mia applicazione potrebbe avere un file di configurazione chiamato config.json :

{
    "cache": {
        "host": "localhost",
        "port": 5555,
        "logErrors": true
    }
}

Tuttavia, sorge un problema (o dovrei chiamarlo trade-off?). L'oggetto è ora abbinato a ConfigurationProvider . Questo può essere in gran parte risolto reimplementando la sua interfaccia, in modo da poter passare qualsiasi configurazione che desideri come oggetto, ma è un sacco di problemi. Naturalmente, posso (e certamente dovrei) creare un sovraccarico costruttore che riceve la configurazione direttamente al posto del provider.

Sebbene possa sembrare che abbia risposto a qualsiasi domanda che potrei avere, la mia domanda è ancora la, c'è un modello, o almeno una convenzione, su questo argomento? È un anti-pattern?

    
posta Bruno Brant 18.08.2018 - 01:05
fonte

1 risposta

2

Congratulazioni, hai reinventato il schema di localizzazione dei servizi . Hai identificato correttamente il suo principale svantaggio: ti accoppia al fornitore.

La tua principale alternativa a questa è l'iniezione di dipendenza. Non del contenitore ma delle dipendenze.

Se il tuo oggetto sa come usare un contenitore non è disaccoppiato da esso.

Quello che vorrei raccomandare è solo di far sapere agli oggetti cose di cui hanno bisogno. In questo modo non finiscono per accoppiarsi a come trovarli.

Questo rispetta la legge di Demetra ma può essere portato lontano.

Il loro è un refactoring chiamato introduce l'oggetto parametro . È simile in quanto riduce al minimo il numero di parametri nei costruttori ma è migliore di localizzatore di servizio perché l'oggetto parametro è personalizzato per come viene utilizzato. Non è una taglia unica adatta a tutto ciò che deve imparare a gestire. È usato solo in alcuni posti. Funziona bene se gli dai un buon nome.

Puoi anche creare metodi di fabbrica che non prendono parametri, creano le dipendenze necessarie e li iniettano nei costruttori lasciando l'oggetto senza tracce su da dove provengono le dipendenze. Puoi avere molte fabbriche che costruiscono questo oggetto usando dipendenze diverse. È facile finchè sei bravo a pensare ai nomi delle fabbriche.

Infine c'è il buon vecchio build tutto in main() pattern. Costruisci la dipendenza dopo la dipendenza. Dai loro tutti i nomi. Passali a ciò che ha bisogno di loro. Quando tutto è stato creato e connesso, inizia il conto alla rovescia chiamando process.start() . Fatto in questo modo, l'unica cosa accoppiata al tuo codice di costruzione è main() stessa. Lo svantaggio? Devi pensare ai bei nomi.

Ci sono schemi che evitano il bisogno di dare nomi alle cose ma mentre rendono felice la CPU raramente rendono felici gli umani.

    
risposta data 18.08.2018 - 01:56
fonte