dependency injection na vida real

Estou construindo uma estrutura MVC muito mínima para aumentar meu conhecimento PHP e me desafiar. Cheguei ao ponto em que as Classes começam a depender umas das outras para trabalhar. A injeção de dependência parece ser a solução para isso e é usada por algumas das grandes estruturas em torno.

Encontrei o Bucket no Github e confundi-lo por um tempo para entender o básico. No entanto, o que eu não consigo entender é quando é apropriado criar um Recipiente?

Fazer um grande recipiente, incluindo todas as classs possíveis que possam ser necessárias, parece nada além de contraproducente para mim e não consigo imaginar que seja uma boa prática. Parece ser a receita para o mau desempenho pelo menos.

Em alternativa, o que é fazer vários recipientes, ainda não consigo como os Singletons nunca tão atraentes não são mais necessários.

Digamos que eu teria o seguinte código:

$session_container = new bucket_Container(); $session_container->create('Database'); $session_container->create('Database_Sessions'); $log_container = new bucket_Container(); $log_container->create('Database'); $log_container->create('Database_Log'); 

Então, aqui temos dois recipientes, ou, neste caso, baldes para dois usos totalmente diferentes que são mútuos por sua dependência na class Database .

Minha lógica me diz que o código acima criará duas instâncias independentes da class Database , o que significa que eu ainda teria que fazer do Database class um singleton para garantir que ocorrências simultâneas da minha conexão de database não estejam ocorrendo?

Isso é correto?

Não sei muito sobre a lib específica, mas assumindo que permite usar uma fábrica, deixe a fábrica retornar a mesma instância.

Editar: Ok, isso é simplesmente na página de índice do Bucket GitHub.

 class MyFactory { function new_PDO($container) { return new PDO("mysql:host=localhost;dbname=addressbook", "root", "secret"); } } $bucket = new bucket_Container(new MyFactory()); $db = $bucket->get('pdo'); 

Então, no seu caso, você poderia simplesmente fazer:

 class MyFactory { private $pdo; function new_Database($container) { if($this->pdo){ return $this->pdo; } return $this->pdo = new PDO("mysql:host=localhost;dbname=addressbook", "root", "secret"); } } $factory = new MyFactory(); $session_container = new bucket_Container($factory); $session_container->create('Database_Sessions'); $log_container = new bucket_Container($factory); $log_container->create('Database_Log'); 

Algo parecido. Não parece ciência do foguete.

Edit2: Não tenho suficientes pontos de representante para comentar a questão (um pouco bobo), mas em resposta à sua preocupação de “modularidade”: pense no recipiente como “cola” de sua aplicação. Na verdade, se você tiver um aplicativo grande, você pode querer “colar” apenas dentro de uma parte isolada de sua aplicação. Essa é uma preocupação de encapsulamento válida. Mas mesmo assim você ainda precisa de um recipiente que manipule injeção no mais alto nível de abstração. Se você apenas criou um recipiente separado para cada parte do seu aplicativo, você acabou com a duplicação desnecessária de instâncias, ou você deve aplicar outro nível de gerenciamento de instância, o que não melhora o encapsulamento de qualquer maneira: você ainda está compartilhando instâncias entre diferentes partes de sua aplicação.

Meu conselho seria usar um único recipiente no nível de boot. Se você deseja adicionar encapsulamento para partes específicas da sua aplicação (módulos, plugins, seja o que for), use “recipientes para crianças”. Um contêiner filho herda as instâncias do recipiente pai, mas o recipiente pai não sabe nada da criança (tanto quanto ele está preocupado, ele ainda é solteiro;)). Poderia ser que o Bucket suporte isso por padrão, eu sei que outros contêineres DI. Caso contrário, é muito fácil implementar usando um decorador. Imagine algo assim:

 class MyContainerType extends bucket_Container { private $_parent; private $_subject; public function __construct($factory = null, bucket_Container $parent = null){ $this->_parent = $parent; $this->_subject = new bucket_Container($factory); } public function get($key){ $value = $this->_subject->get($key); if($value){ return $value; } return $this->_parent->get($key); } /** * Override and delegation of all other methods */ } 

Fazer um grande recipiente, incluindo todas as classs possíveis que possam ser necessárias, parece nada além de contraproducente para mim e não consigo imaginar que seja uma boa prática. Parece ser a receita para o mau desempenho pelo menos.

Pelo contrário. Isto é exatamente o que você faria com um di container. O recipiente apenas instanciará objects sob demanda, portanto, praticamente não há despesas gerais para gerenciar todas as classs de singleton-ish através dele.

O maior problema com di é distinguir entre objects compartilhados (coisas que você costuma pensar como singletons) e objects transitórios (Objetos que têm muitas instâncias através de um stream de aplicativos normal). Os primeiros são facilmente gerenciados através de di. O último não se encheckbox. Ter esses dois “tipos” de objects claramente distinguidos pode parecer um pouco um aborrecimento, mas é realmente um efeito colateral altamente benéfico de usar um recipiente di.

Se você está preocupado com várias conexões simultâneas, você pode usar mysql_pconnect () ou o equivelant para o database que está usando. Ele verificará se uma conexão já está aberta e use a conexão existente se for.

No que diz respeito ao problema dos contêineres, eu vi isso feito de duas maneiras, o que parece estar ciente de ambos. O primeiro método é ter a estrutura ler seu esquema de database e criar classs para cada tabela. Eu, pessoalmente, não gosto dessa abordagem. Symfony é uma estrutura que faz isso (usando a doctrine ORM).

O método mais preferido que eu vi é ter um recipiente genérico, que basicamente cria o sql para você, dado uma tabela, colunas e uma ação. Esta é a abordagem tomada pelo códigoIgniter:

 $query = $this->db->get('mytable'); $query = $this->db->get_where('mytable', array('id' => $id), $limit, $offset);