2010-03-24 1 views
9

Sto iniziando un nuovo progetto e sto configurando la base su cui lavorare. Alcune domande sono sorte e probabilmente ne farò alcune qui, spero di trovare qualche risposta.Accesso al contenitore DI

Il primo passaggio consiste nel gestire le dipendenze per gli oggetti. Ho deciso di andare con il modello di progettazione di dipendenza dipendenza, a cui sono un po 'nuovo, per gestire tutto questo per l'applicazione.

Quando in realtà la codifica mi sono imbattuto in un problema. Se una classe ha più dipendenze e si desidera passare più dipendenze tramite il costruttore (in modo che non possano essere modificate dopo aver istanziato l'oggetto).

Come si fa senza passare una serie di dipendenze, utilizzando call_user_func_array(), eval() o Reflection? Questo è quello che sto cercando:

<?php 

class DI 
{ 
    public function getClass($classname) 
    { 
     if(!$this->pool[$classname]) { 
      # Load dependencies 
      $deps = $this->loadDependencies($classname); 

      # Here is where the magic should happen 
      $instance = new $classname($dep1, $dep2, $dep3); 

      # Add to pool 
      $this->pool[$classname] = $instance; 

      return $instance; 
     } else { 
       return $this->pool[$classname]; 
     } 
    } 
} 

Ancora una volta, vorrei evitare i metodi più costosi per chiamare la classe. Qualche altro suggerimento?

Inoltre, come accedere alla classe DI all'interno di classi, ad esempio, in controller che devono accedere a modelli diversi? Dovrei chiamarlo staticamente o passarlo lungo ogni classe che lo richiederebbe? Non penso che l'ultima idea sia fattibile.

Grazie a tutti.

risposta

21

[Prima di iniziare, lasciatemi dire che sono principalmente un programmatore Java, con solo un po 'di conoscenza di PHP. Ma io semplicemente cerco di ottenere i concetti più importanti in tutto, senza specificità linguistiche]

Dependency Injection si basa su due parti di codice:.

  1. Edilizia
  2. Esecuzione

In la sua forma più estrema, non ci sono operatori new da trovare nella parte Esecuzione. Tutti loro sono trasferiti nella parte di costruzione. (In pratica, questo sarà attenuato.)

Tutte le costruzioni avvengono - nella parte di costruzione. Crea il grafico degli oggetti necessari per l'esecuzione dal basso verso l'alto. Quindi supponiamo, dovrebbe costruire un:

  • A dipende da B, e
  • B dipende C.

Poi

  • C è costruito prima.
  • Quindi B è costruito con C come parametro.
  • Quindi A è costruito con B come parametro.

Quindi C non deve essere passato come parametro costruttore ad A. Questo piccolo esempio non illustra abbastanza fortemente, quanto questo riduce la quantità di oggetti che devono essere passati a un numero abbastanza piccolo numero.

Lo stesso iniettore di dipendenza non deve essere trasferito nella parte di esecuzione. Questo è uno degli errori fondamentali che tutti (incluso me stesso) cercano di fare, quando entrano in contatto per la prima volta con DI. Il problema è che questo annullerebbe completamente le linee tra Costruzione ed Esecuzione. Un altro modo per dirlo è che violerebbe lo Law of Demeter. O nel pattern speak: alla fine "degraderebbe" il pattern dell'iniezione di dipendenza nel pattern di localizzazione del servizio. È discutibile, se questo è davvero un degrado, ma in ogni caso di solito non è una buona idea abusare del filtro di dipendenza come localizzatore di servizio.

Quindi, ogni volta che è necessario assegnare a uno degli oggetti costruiti la possibilità di produrre altri oggetti durante l'esecuzione, anziché passare l'iniettore di dipendenze, si passa solo a provider semplici (un termine utilizzato dal framework DI ID Guice). Queste sono classi piuttosto semplici che possono solo creare un certo tipo di oggetto. Hanno somiglianze con una fabbrica.

Prima prova a passare le dipendenze richieste direttamente al costruttore.

Quindi, per riassumere:

  • costruire oggetti bottom-up.
  • Passa solo il minor numero di dipendenze necessarie per creare un oggetto.
  • Una volta terminato, iniziare l'esecuzione.
  • Durante l'esecuzione, è ancora possibile recuperare gli oggetti appena creati utilizzando i provider.

Ma non prenderlo troppo lontano: oggetti semplici possono ancora essere creati senza un provider :-)

E ora, tutto quello che dovrete fare è di tradurre questa roba in codice di qualità. Forse altri possono aiutarti con alcuni esempi di PHP.

Addendum: Un po 'di più su Provider

Come notato sopra, la nozione di "Provider" (una fabbrica specializzata) è un po' specifico per il framework Java DI Guice. Questo framework può creare automaticamente un Provider per qualsiasi tipo di oggetto. Tuttavia, il concetto è generalmente utile per DI. L'unica differenza è che senza l'aiuto di Guice o di un framework simile, dovrai scrivere tu stesso i provider, ma è abbastanza semplice:

Diciamo che B dipende da C.

  • Se B ha solo bisogno di una sola istanza fissa di C, quindi non è necessario un provider - si può semplicemente costruire B con l'argomento del costruttore C.
  • Se B ha bisogno di creare più istanze di C durante l'esecuzione , quindi basta scrivere una classe chiamata CProvider con un metodo get(), che può creare una nuova istanza di C. Quindi passare un'istanza di CProvider nel costruttore di B e memorizzare il Provider in un campo istanza di B. Ora B può chiamare cProvider.get() quando ha bisogno di una nuova istanza di C.

I provider sono parte del codice di costruzione, quindi è consentito utilizzare new C(...)! D'altra parte, non fanno parte del codice di esecuzione, quindi non dovresti avere alcuna logica di esecuzione.

CProvider possono essere passati a più costruttori, ovviamente. Puoi anche scrivere più versioni CProvider1, CProvider2, ... - dove ognuno può costruire versioni diverse di oggetti C con proprietà differenti. Oppure è semplice istanziare CProvider più volte con argomenti diversi.

+0

Insightful, grazie per la spiegazione. Conosco il modello di Service Locator e non è quello che sto cercando di realizzare. Potresti per favore elaborare un po 'sull'uso dei provider? – Andre

+0

Aggiunta di un addendum. Fammi sapere, se hai bisogno di maggiori informazioni. –

+0

Grazie mille per l'aiuto. Fondamentalmente ho bisogno di passare i fornitori richiesti per ogni dipendenza a più istanze. Questi sono fondamentalmente modelli di fabbrica ma specifici per creare oggetti per uno scopo (potrebbe essere una varietà di classi, un esempio sarebbe driver di output) e restituirli alla classe dipendente. Sembra più complessità in arrivo: D – Andre

2

Si dovrebbe esaminare utilizzando un contenitore IOC per gestire le dipendenze per voi. Un buon contenitore di IOC dovrebbe occuparsi di passare le dipendenze tra i dipendenti dipendenti.

C'è uno question esistente che richiede le opzioni del contenitore IOC per PHP.

2

Sembra che tu stia cercando di eseguire il rollover del tuo contenitore per le dipendenze. Perché non utilizzarne uno già esistente, ad esempio Symfony, Crafty o Sphicy?

+0

L'obiettivo è evitare l'utilizzo di librerie di terze parti se non strettamente necessario (principalmente a causa di problemi di licenza). – Andre

+0

@andre: Symfony è concesso in licenza con la licenza MIT. Leggilo, in pratica dice "fai quello che vuoi": http://www.opensource.org/licenses/mit-license.php –

+2

Sto creando la mia licenza e non voglio dare l'attribuzione alle librerie di terze parti o software (parte dei requisiti del progetto), in quanto tale ho bisogno di creare il mio contenitore. – Andre