2013-01-21 11 views
8

Stiamo appena iniziando a fare uno sforzo concertato per utilizzare l'iniezione di dipendenza in modo uniforme nel nostro progetto e ho riscontrato un problema.Iniezione di dipendenze PHP quando gli argomenti per il costruttore non sono disponibili

Sto scrivendo una classe per gestire le query di MongoDB. Passo in MongoClient come dipendenza dal costruttore, senza problemi. Ma come gestisco una dipendenza quando la variabile necessaria per istanziare l'oggetto non è disponibile al momento dell'istanziazione?

In particolare, abbiamo un wrapper per il metodo MongoCollection, findOne, che, se passi una stringa, attualmente (nel vecchio codice) trasforma quella stringa in un MongoId con "new MongoId ($ _id)", e lo usa per la funzione di ricerca.

Da quello che ho imparato sull'iniezione delle dipendenze, avere "nuovo MongoId" è una cattiva idea, e so già che renderà più difficile scrivere casi di test per la funzione che converte una stringa in un MongoId.

Ma come gestisco l'iniezione, quando la classe MongoId prende la stringa id sul costruttore?

L'unica cosa che ho pensato che avrebbe funzionato è quello di passare una chiusura sul costruttore di classe che fa qualcosa di simile:

$getMongoId = function($id){ 
    return new MongoId($id); 
}; 

con

class MyMongo 
{ 
    function __construct(MongoClient $client, Closure $mongoIdGetter){...} 
} 

[modificato per risolvere questo problema ultima parte]

Ma è questo il modo giusto per gestirlo? Certamente, se stiamo usando un DiC, possiamo farlo, ma richiedere una chiusura per il costruttore sembra un po 'troppo. Sono semplicemente troppo dogmatico per aver iniettato le mie dipendenze? Potrei sistemarlo facilmente usando "new MongoId ($ _id)" nella nuova classe, suppongo.

risposta

1

Ma come si gestisce una dipendenza quando la variabile necessaria per creare un'istanza dell'oggetto non è disponibile al momento dell'istanziazione?

PHP sarà un errore fatale prima che tu abbia la possibilità di gestirlo da solo. Se si utilizzano i parametri di tipo e/o non li si definiscono come, per impostazione predefinita, il codice di errore null PHP sarà fatale quando tale parametro non viene passato a nessuna funzione.

Da quello che ho imparato a conoscere l'iniezione di dipendenza, avendo "nuova MongoId" è una cattiva idea, e so già che renderà più difficile per scrivere i test per la funzione che converte una stringa in un MongoId .

Lo sarà (in PHPUnit)?

$this->assertInstanceOf('\MongoId', $getMongoId($id_string)); 

Ma come faccio a gestire l'iniezione, quando la classe MongoId prende la stringa id sul costruttore?

Non so cosa intendi, ma dovresti testare solo il risultato dell'elaborazione dello MongoId s.

L'ultimo bit della tua domanda mi perde un po ', penso sia perché non è vero PHP (ad esempio $__construct).

Non sono sicuro del motivo per cui è necessario inserire la funzione nella classe in questo modo. Voglio dire quello che ho la maggior parte delle volte è:

function findById($id){ 
    if(!$id instanceof \MongoId) $id = new MongoId($id); 
    return $this->getCollection()->findOne($id); 
} 

Non avete bisogno di qualcosa di più di quello e non è necessario per testare il costruttore di MongoId dal momento che l'apparecchio è già stato testato, si dovrebbe essere invece unità testare la tua API pubblica non di qualcun altro.

+0

Grazie - il costrutto $ __ era solo una scoreggia cerebrale, fissata sopra. Penso che in parte hai letto male la mia domanda perché quando dico "gestire" sopra, non mi riferisco alla gestione degli errori, ma solo al senso generale del termine. – Karptonite

+0

@Karptonite Oh ok, quindi puoi chiarire il motivo per cui vuoi inserire il setter MongoId nel costrutto, capisco la dipendenza ma non sarà necessario finché non utilizzerai effettivamente una funzione 'find'. Intendo per il test dell'unità tutto ciò che serve è controllare la risposta della funzione pubblica che restituisce un documento che dovrebbe esistere mentre si introduce una stringa per '_id', non si dovrebbe estendere il test dell'unità al driver MongoDB stesso – Sammaye

+0

Quindi Sono nuovo all'iniezione di dipendenza, ma la mia comprensione era che usare una funzione come quella che hai scritto, con "new MongoId" nel codice, era generalmente una cattiva idea - ad esempio, se usavo metodi di MongoId nel mio codice, non potevo prenderlo in giro per assicurarmi che il mio codice chiamasse correttamente quei metodi. Ammetto che non è qualcosa che sto facendo ora. – Karptonite

2

Invece di una chiusura è possibile utilizzare un fabbrica:

class MongoFactory 
{ 
    public function createMongoDb($id) 
    { 
     return new MongoId($id); 
    } 
} 

nelle fabbriche è considerato bene avere "nuove qualcosa di" dipendenze hard coded, perché la creazione di oggetti è il loro unico scopo e si potrebbe facilmente sostituire loro con un'altra fabbrica.

La classe dei consumatori (MyMongo) ora avrà una dipendenza a MongoFactory (o la sua interfaccia, se si vuole), che si può facilmente "iniettare".

Problemi correlati