2015-09-10 22 views
14

私はDIIoCを頭に浮かべています。今のところPimpleを使用しています。私は新しいオブジェクトがIoCを意識せずにインスタンス化されますが、それでも私は再利用したいと思いますcall_user_func_arrayIoCコンテナを渡す適切な方法

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

ことを早期に実行フロー

$container = new Injection\Container(); 

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

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

... 

とルータクラスで私のIoCを定義したとしましょういくつかのサービスが定義されています。

  1. クラス(静的は悪のようです...)または
  2. IoCを渡すために適切な方法で合格することが必要でさえですがどうなるか:私の質問がある

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

    周囲の容器や他の方法/概念が存在するか?

しかし

をそれを把握することができませんでした、いくつかの素晴らしい記事を読む更新

方法I少なくとも有害な方法を使用することを決めたが

Update 2の静的プロパティでIoC

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

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

以降

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

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

でのアクセス、それを保存し、別のサービスを定義していた

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

そして__constructor

public $container; 

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

答えて

11

でパラメータを格納し、両方のための2つのIoCのために使用され、一般的にあるパターン、および支持者があります。

  1. 依存性注入
  2. サービスロケータ

しかし、より多くのDIは、サービスロケータパターンの上に出て勝っているようです。多くのDIコンテナは、Service Locatorを使用することをより困難にしており、ドキュメントの警告がその道を塞がないようにしています。

は、クラスがcomposition rootの一部でない限り、クラスにコンテナを渡しません。コンポジションルートは、アプリケーションのエントリーポイントでオブジェクトグラフを解決することで動きのすべてを開始し、そこからアプリケーションはDIコンテナを完全に認識しません(参照はありません)。このオブジェクトグラフには、クラスの実行時インスタンスを作成するAbstract Factoriesが含まれている可能性があります(DIコンテナから解決する関数を注入するか、単にそれらを新しいものにすることによって)。

サービスロケータはスペクトルの反対側です。通常、コンテナはスタティックにするか、唯一の依存関係としてクラスに渡します。このようにクラスを作成する方が簡単かもしれませんが、実際ににDIコンテナを設定する必要がある場合は、後で代金を支払うことになります。

DIの場合、クラスの依存関係は明示的であるため、コンストラクタのパラメータよりも詳細を見る必要はありません。 Service Locatorを使用すると、依存関係の設定ははるかに複雑になります。これが主な理由(実際には多くの理由があります)なぜ近年ではanti-patternと考えられるのですか。

質問に答えるために、IoCの現代的なアプローチに従う場合は、IoCコンテナをアプリケーションに渡さないでください。代わりに、アプリケーションのエントリポイントでコンポジションルートを使用してコンテナを設定し、オブジェクトグラフを構築します。

依存性注入例

PHPで完全な例はhereを見ることができる

。そのページは a discussion on the Pimple projectにあります。あなたはとてもあなたのクラスは、そのインスタンスを受け取りますあなたの$要求サービスを受け入れるようにコンストラクタを追加する必要があります

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

だから、あなたの例を与えます。

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() 
    } 
} 

次に、コンポジションルートにコントローラを定義するセクションがあります。これは、依存関係が注入される場所です。

$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'] 
    ); 
}); 

は、あなたが見ることができるように、このアプローチを使用して、あなたのDogクラスは、DIコンテナを全く知らないです。

+0

ありがとうございます。また、「DIでは、そのクラスがコンポジションルートの一部でない限り、コンテナをクラスに渡すことはありません。と "DIでは、クラスの依存関係は明示的なので、コンストラクタのパラメータよりも見た目を変える必要はありません。"私の場合に適用されます。しかし、クラスが未知のサービスに依存している場合はどうでしょうか?セッター注入? – sitilge

+0

'クラスがまだ不明なサービスに依存する場合はどうすればよいですか? ' - IoCの使用法は*具体的な型ではなく*抽象クラス*へのプログラミングです。これは多くの点で役立ちますが、主に未知の具体的なクラスをコードを変更せずにプラグインする方法を提供することができます。また、テスト用のモックを作成することもできます。クラスが抽象規約(通常はインターフェース)に準拠している限り、これは可能です。私はスニペットを提供したいと思いますが、私はPHPに慣れていません... – NightOwl888

+0

ありがとうございますが、 'call_user_func_array'を使用しているので、これは問題を解決しません。 – sitilge

2

ここでは、工場、シングルトン、依存インジェクタ、サービスロケータ、インターフェイスインジェクタなどのパターンを使っています。

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', 
    ] 
]; 

サービスコンテナ

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 ]; 
    } 

} 

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; 

私は件名に説明し、私の意見を書くことについて考えてきたが、私は」ので、これらの話題については、あまりにも多くの意見があります。嫌なことや何かを防ぐこと)、私は例を残すだけで、誰かにとって有用かもしれません。