Come utilizzare i contenitori DI e DI

6

Sto costruendo un piccolo framework PHP in mvc (sì, ancora un altro), principalmente per scopi di apprendimento, e sto cercando di farlo nel modo giusto, quindi mi piacerebbe usare un contenitore DI, ma non lo sono chiedendo quale usare ma piuttosto come usarne uno.

Senza entrare troppo nel dettaglio, il mvc è diviso in moduli che hanno controller che visualizzano le viste per le azioni. Ecco come viene elaborata una richiesta:

  • un oggetto principale crea un'istanza dell'oggetto Request e un router e inietta la richiesta nel router per capire quale modulo è stato chiamato.
  • quindi crea un'istanza dell'oggetto Module e invia la richiesta a tale
  • il Modulo crea un ModuloRouter e invia la Richiesta per capire il controllore e l'azione
  • crea quindi il controller e il ViewRenderer e inietta ViewRenderer nel controller (in modo che il controller possa inviare i dati alla vista)
  • ViewRenderer deve sapere quale modulo, controller e azione sono stati chiamati per capire il percorso degli script di visualizzazione, quindi il Modulo deve capire questo e iniettarlo nel ViewRenderer
  • il Modulo chiama quindi il metodo di azione sul controller e chiama il metodo di rendering su ViewRenderer

Per ora, non ho alcun contenitore DI configurato, ma quello che ho sono un mucchio di metodi initX () che creano il componente richiesto se non lo sono già. Ad esempio, il modulo ha il metodo initViewRenderer (). Questi metodi init vengono richiamati subito prima che quel componente sia necessario, non prima, e se il componente era già impostato non lo inizializzerà. Ciò consente di commutare i componenti, ma non è necessario impostarli manualmente se non ci sono.

Ora mi piacerebbe farlo implementando un contenitore DI, ma mantenendo comunque la configurazione manuale al minimo, quindi se la struttura della directory e la convenzione di denominazione sono seguite, tutto dovrebbe funzionare, senza nemmeno toccare la configurazione.

Se utilizzo il contenitore DI, lo inserisco quindi in ogni cosa (il contenitore si inietterà automaticamente durante la creazione di un componente), in modo che altri componenti possano utilizzarlo? Quando registro i componenti con il DI? Un componente può registrare altri componenti con la DI durante l'esecuzione? Creo una configurazione "comune" e la uso? Come faccio quindi a capire al volo quali componenti ho bisogno e come devono essere impostati?

Se Main usa il router che usa Request, Main deve quindi utilizzare il contenitore per ottenere il modulo (o il modulo deve essere trovato e impostato in anticipo? Come?) Il modulo utilizza il router, ma ha bisogno di capire le impostazioni per ViewRenderer e il controller al volo, non in anticipo, quindi il mio contenitore DI non può impostare quelli sul modulo prima che il modulo calcoli il controller e l'azione ...

Cosa succede se il controller necessita di qualche altro servizio? Inietto il contenitore in ogni controller? Se comincio a farlo, potrei semplicemente iniettarlo in tutto ...

Fondamentalmente sto cercando le migliori pratiche quando si tratta di cose come questa. So cosa è DI e cosa fanno i contenitori DI, ma sto cercando una guida per usarli nella vita reale, e non alcuni esempi isolati sulla rete.

Ci scusiamo per il lungo post e molte grazie in anticipo.

EDIT: dopo aver letto un po 'di più, sembra che iniettare il contenitore stesso in oggetti e farli usare il contenitore per trovare roba sia in realtà una posizione di servizio, non un'iniezione di dipendenza. Quindi la mia ultima idea è di usare le fabbriche per le cose che dovrebbero essere acquisite dinamicamente sulla base di qualcosa (come ottenere controllori basati su ciò che dice il router), e il contenitore verrebbe iniettato nelle fabbriche dove sarebbe usato più come un localizzatore di servizi . I componenti non dinamici verrebbero registrati in anticipo e quei componenti verrebbero creati dal contenitore e resi completamente operativi, con tutte le loro dipendenze riempite.

Quindi il flusso sarebbe simile a questo:

  • ottiene Main dal container, Main viene iniettato con Request, Router e ModuleFactory che utilizza il container per trovare i moduli. Questo è tutto preconfigurato
  • Main utilizza ModuleFactory per creare il modulo, iniettato con ModuleRouter, Request, ControllerFactory e ViewRenderer.
  • Modulo utilizza ControllerFactory per creare un controller, iniettato con qualsiasi servizio da cui dipende, quindi il modulo chiama l'azione sul controller e invia il risultato al ViewRenderer per eseguire il rendering della vista.

Sto ottenendo ovunque con questo?

    
posta Ivan Pintar 02.09.2012 - 21:24
fonte

2 risposte

2

Dato che non c'è alcun giusto o sbagliato in questa domanda, potrei dare un esempio su come mi piace usare Dependency Injection in PHP.

Sto utilizzando il componente Iniezione dipendenze da Symfony con LosoDiAnnotationsBundle ed estrae tutto dal Bundle per usarlo come singolo come componente dell'iniezione di dipendenza. Non sto utilizzando il resto del framework Symfony.

Ci sono due funzioni nel mio sistema che usano il contenitore di iniezione delle dipendenze. La funzione principale e il dispatcher. La funzione principale (punto di ingresso dell'applicazione) recupera il Dispatcher configurato in questo modo:

$dispatcher = $service_container->get('Dispatcher');
$response = $dispatcher->route( $request );

Il servizio chiamato "Dispatcher" è definito nella configurazione di dipendenza globale o annotato nella classe stessa in questo modo:

 /*
 * @Service
 */
 class Dispatcher {
    /**
     * @Inject({"@service_container"})
     */
    public function __construct($serviceContainer) {
        $this->serviceContainer  = $serviceContainer;
    }
 }

Il Dispatcher ha bisogno di questo localizzatore di servizi perché questo è il modo in cui funziona il mio routing. Se usi qualche mappatura extra tra route e metodi chiamati, puoi evitare questo.

Per iniettare qualcosa in altri servizi, utilizzo l'annotazione @inject :

/** @Inject({"@SomeLogic", "@SomeGateway"}) */
public function setDependencies(SomeLogic $someLogic, SomeGateway $someGateway) {
    $this->someLogic = $someLogic;
    $this->someGateway = $someGateway;
}

Mi piace questo approccio perché la configurazione di una classe di servizio viene eseguita nella classe stessa e non in un file separato. Il refactoring non comporta problemi con file XML o YAML lunghi.

Per non danneggiare la performance eseguo uno script ogni volta che cambio alcune annotazioni e al momento della compilazione (o in generale su ogni vagabondo). Usa Symfony's ContainerBuilder e PHP Dumper per generare un file di ricerca PHP che estende la classe Base ServiceContainer.

La ricerca per il dispatcher di esempio sarebbe simile a questa:

protected function getDispatcherService()
{
    return $this->services['dispatcher'] = new \Some\Dispatcher($this);
}

Questo sembra un sacco di lavoro in anticipo, ma separa chiaramente le preoccupazioni in seguito, specialmente nei progetti più grandi. Le classi fanno ciò che dovrebbero fare e non hanno bisogno di un codice logico non di dominio minimo.

E cerco di mantenere le fabbriche a un livello assolutamente minimo. Il contenitore DI è di per sé un unico grande stabilimento e repository ma non è gestito manualmente.

EDIT:

La mia funzione di routing assomiglia a questa, request_uri è qualcosa come Controller/action/...

    $path = explode( self::ACTION_SEPARATOR, $request_uri );

    if( empty( $path[1] ))
        throw new RoutingException( 'Invalid Action' );

    $method = ucfirst( $path[1] );
    $className = $path[0].'Controller';
    $qualifiedClassName = 'Some\Namespace\'.$className;

    if( !class_exists( $qualifiedClassName ))
        throw new RoutingException(sprintf('Invalid controller "%s" for route "%s"', $qualifiedClassName, $route));
    if( !is_callable( array( $qualifiedClassName, $method ) ))
        throw new RoutingException(sprintf('Invalid Action "%s"', $method));


    $controller = $this->serviceContainer->get( $className );
    call_user_func( array( $controller, $method ), $message );
    
risposta data 03.09.2012 - 18:04
fonte
1

Conosco il suo tipo di contro-argomento, ma penso che sarebbe utile per te guardare oltre il DI.

Pensa alle proprietà di DI e a cosa stai cercando di fare con l'applicazione.

Ecco un video, vengono discussi diversi aspetti degli stili di programmazione (intorno a 25 minuti, il caso per DI è fatto) link

    
risposta data 04.09.2012 - 21:59
fonte

Leggi altre domande sui tag