2015-09-10 25 views
14

Sto capendo il numero DI e IoC; utilizzando Pimple per ora. Diciamo che ho il mio IoC definito all'inizio nell'esecuzione flussoModo corretto per passare il contenitore IoC

$container = new Injection\Container(); 

$container['config'] = function ($c) { 
    return new Config($c['loader']); 
}; 

$container['request'] = function ($c) { 
    return new Request($c['config']); 
}; 

... 

E una classe router che call_user_func_array

//$class = 'Dog', $method = 'woof', $this->args = ['foo', 'bar'] 
call_user_func_array(array(new $class, $method), $this->args); 

Quindi nuovo oggetto viene creata un'istanza senza essere a conoscenza del IoC ma ancora vorrei riutilizzare alcuni dei servizi definiti.

class Dog 
{ 
    public function woof($var1, $var2) 
    { 
     //$request = IoC service here 
    } 
} 

La mia domanda è:

  1. Quale sarebbe il modo corretto per passare il IoC alla classe (statica sembra essere il male ...) o
  2. E 'anche necessario superare il contenitore intorno e altri metodi/concetti esistono?

leggere alcuni articoli piacevoli, tuttavia, non è stato in grado di capirlo

Aggiornamento

Il male modo I ha fatto che definiva un altro servizio che salva il IoC in una proprietà statica

$container['services'] = function ($c) { 
    return Services::create($c); //make the service 
}; 

$container['services']; //call the service 

e accedervi in ​​seguito in aggiornamento

class Dog 
{ 
    public function woof($var1, $var2) 
    { 
     $services = new Services(); 

     $request = $services['request']; //retrieving the request service 
    } 
} 

ha deciso di utilizzare il modo meno dannoso

//passing the container here 
call_user_func_array(array(new $class($container), $method), $this->args); 

e memorizzare i parametri in __constructor

public $container; 

public function __construct(Injection\Container $container) 
{ 
    $this->container = $container; 
} 

risposta

11

Ci sono 2 modelli che sono comunemente in uso per CIO, e sostenitori per entrambi.

  1. Dependency Injection
  2. Service Locator

Tuttavia, sembra che sempre più sta vincendo DI affaccia sul modello Service Locator. Molti contenitori DI rendono più difficile l'uso di Service Locator e gli avvisi nella documentazione di non andare giù per quella strada.

In DI, si mai passare il contenitore a una classe a meno che tale classe sia parte di composition root.La root di composizione avvia tutto in movimento risolvendo il grafo degli oggetti nel punto di ingresso dell'applicazione e da lì l'applicazione è completamente inconsapevole del contenitore DI (non ha alcun riferimento ad esso). Si noti che questo grafico oggetto può contenere Abstract Factories che crea istanze di classi di runtime (iniettando una funzione per risolvere il contenitore DI o semplicemente aggiungendole).

Service Locator è l'altra estremità dello spettro. In genere, il contenitore viene reso statico o passato a una classe come unica dipendenza. Potrebbe essere più semplice creare classi in questo modo, ma pagherai il prezzo più tardi quando devi effettivamente configurare nel contenitore DI.

Con DI, le dipendenze di una classe sono esplicite quindi non è necessario guardare oltre i parametri del costruttore. Con Service Locator, la configurazione delle dipendenze è molto più complicata. Questo è il motivo principale (ci sono in realtà molte ragioni) perché negli ultimi anni è considerato uno anti-pattern.

Quindi, per rispondere alla domanda, se si desidera seguire l'approccio moderno a IoC, non passare il contenitore IoC nell'applicazione. Al contrario, utilizzare una radice di composizione nel punto di ingresso dell'applicazione per configurare il contenitore e creare il grafico dell'oggetto.

iniezione di dipendenza Esempio

Un esempio completo in PHP può essere visto here. Ho trovato quella pagina in a discussion on the Pimple project.

Quindi, dato il vostro esempio:

class Dog 
{ 
    public function woof($var1, $var2) 
    { 
     //$request = IoC service here 
    } 
} 

Si avrebbe bisogno di aggiungere un costruttore di accettare la vostra richiesta servizio $ modo la classe riceverà un'istanza di esso.

class Dog 
{ 
    protected $request; 

    public function __construct(Request $request) 
    { 
     $this->request = $request; 
    } 

    public function woof($var1, $var2) 
    { 
     //Use $request here, disregard the IoC container 
     $this->request->doSomething() 
    } 
} 

E quindi si dispone di una sezione in cui si definiscono i controller nella radice di composizione. Qui è dove vengono iniettate le dipendenze.

$container = new Injection\Container(); 

$container['config'] = function ($c) { 
    return new Config($c['loader']); 
}; 

$container['request'] = function ($c) { 
    return new Request($c['config']); 
}; 

$container['dog'] = $container->factory(function ($c) { 
    return new Dog($c['request']); 
}); 

$container['user_controller'] = $container->share(function ($container) { 
    return new UserController(
     $container['dog'] //, 
     // $container['someOtherDependency'] 
    ); 
}); 

Come si può vedere, utilizzando questo approccio la classe Dog è completamente ignaro del contenitore DI.

+0

Grazie ed è d'accordo con te; anche "In DI, non si passa mai il contenitore a una classe a meno che quella classe non sia parte della composizione root." e "Con DI, le dipendenze di una classe sono esplicite quindi non è necessario guardare oltre i parametri del costruttore". si applica al mio caso.Ok, ma cosa succede se la classe dipende da servizi che sono ancora sconosciuti? Iniezione setter? – sitilge

+0

'Cosa succede se la classe dipende da servizi che sono ancora sconosciuti?' - L'uso di IoC riguarda la programmazione di * astrazioni *, non * tipi di calcestruzzo *. Ciò aiuta in molti modi, ma principalmente in modo da poter fornire un modo per future classi concrete sconosciute da collegare senza modificare il codice. Rende anche possibile creare mock per il test. Finché la classe aderisce al contratto astratto (solitamente un'interfaccia), questo è possibile. Vorrei fornire uno snippet, ma non ho familiarità con PHP ... – NightOwl888

+0

grazie, ma questo non risolve il problema dal momento che sto usando 'call_user_func_array' e solo le cose che vengono passate sono parametri url. – sitilge

2

Ecco un esempio del mio scherzo con fabbrica, singleton, iniettore di dipendenza, localizzatore di servizio, iniettore di interfaccia e altri modelli.

config.php

return [ 
    'services' => [ 
     'request' => 'Request', 
     'view' => 'View', 
     'db' => function() { 
      return new DB('host', 'username', 'password', 'dbname'); 
     }, 
     'translator' => function($sm, $config) { 
      return new Translator($sm->db, $config); 
     }, 
     'model' => function() { 
      return new ModelFactory($sm);   
     } 
    ], 
    'interfaces' => [ 
     'iService' => function($object, $sm) { 
      $object->sm = $sm; 
     } 
    ], 
    'translator' => [ 
     'locale' => 'en_US', 
    ] 
]; 

contenitore di servizio

class ServiceManager { 
    private $services, $interfaces, $params; 

    function __construct($config) 
    { 
     foreach($config[ 'services' ] as $name => $value) 
      $this->add($name, $value, isset($config[ $name ]) ? $config[ $name ] : null); 

     $this->interfaces = isset($config[ 'interfaces' ]) ? $config[ 'interfaces' ] : null; 
    } 

    function add($name, $service, $params = null) 
    { 
     $this->services[ $name ] = $service; 
     $this->params[ $name ] = $params; 
    } 

    function __get($name) 
    { 
     if (is_string($this->services[ $name ])) 
      $this->services[ $name ] = new $this->services[ $name ]($this->params[ $name ]); 

     if (is_callable($this->services[ $name ])) 
      $this->services[ $name ] = $this->services[ $name ]($this, $this->params[ $name ]); 

     foreach($this->interfaces as $interface => $value) { 
      if ($this->services[ $name ] instanceof $interface) 
       $value($this->services[ $name ], $this);     
     } 

     return $this->services[ $name ]; 
    } 

} 

esempio

interface iService {} 

class View implements iService { 
    function render() { 
     print_r($this->sm); 
    } 
}; 

class DB { 
    function __construct($host, $username, $password, $dbname) {} 
}; 

class Translator { 
    function __construct($db, $config) {} 
}; 

class ModelFactory { 
    function __construct($sm) {} 
} 

class App { 
    function __construct() { 
     $sm = new ServiceManager(include 'config.php'); 
     $sm->view->render(); 
    } 
} 

new App; 

ho pensato di scrivere una spiegazione e la mia opinione sul soggetto, ma da quando ho' Non sono così bravo a scrivere e ci sono già troppe opinioni su questi argomenti (gran parte di loro sono a a proposito di cattiveria e anti-qualcosa), lascerò solo l'esempio, potrebbe essere utile a qualcuno.