使用pimple作为我的DI容器,我一直在勇敢地重构小类以依赖DI注入,消除了我可以看到的易于删除的硬编码依赖。
我完成这项任务的方法非常简单,但是我不知道它是否合适,因为除了上个月我在这里学到的东西之外,我对DI和单元测试的经验很少。
我创建了一个类,ContainerFactory,它是pimple的子类,并且在该子类中创建了只返回特定对象容器的方法。
构造函数根据类型调用正确的创建者方法:
function __construct($type=null, $mode = null){
if(isset($type)){
switch ($type) {
case 'DataFactory':
$this->buildDataFactoryContainer($mode);
break;
case 'DbConnect':
$this->buildDbConnectContainer($mode);
break;
default:
return false;
}
}
}
容器对象创建的方法签名如下:
public function buildDataFactoryContainer($mode=null)
我的想法是,我可以在调用此容器时设置$ mode进行测试,并让它加载测试值而不是实际的运行时设置。我想避免编写单独的容器类进行测试,这是一种我认为没有全部测试相关代码的简单方法。
我可以改为继承ContainerFactory,即:ContainerFactoryTesting extends ContainerFactory
并覆盖其中,而不是将测试代码与app代码混合,并使用$ mode = null混合方法签名,但这不是本文的重点。继续,为特定对象创建容器,我只是这样做:
// returns container with DataFactory dependencies, holds $db and $logger objects.
$dataFactoryContainer = new ContainerFactory('DataFactory');
// returns container with test settings.
$dataFactoryTestContainer = new ContainerFactory('DataFactory','test');
// returns container with DbConnect dependencies, holds dbconfig and $logger objects.
$dbConnectContainer = new ContainerFactory('DbConnect');
我刚遇到一个让我怀疑我正在构建的策略存在缺陷的问题。
查看上面的内容,DataFactory包含保存数据库连接的$ db对象。 我现在正在重构这个dbclass以删除它对$ registry对象的依赖, 但是当我添加需要$ dbConnectContainer的$ db对象时,我将如何创建$ dataFactoryContainer?
例如,在datafactory容器中我添加了一个dbconnect实例,但IT现在需要传递一个容器...
我意识到我的英语不是那么好,希望我已经足够好地解释了一个让他们理解的人。
我的问题是双重问题,你们如何以简单的方式处理为自身包含依赖项的依赖项创建对象?
并且..为了测试目的,如何分离容器配置以创建对象?
与往常一样,对相关帖子的任何评论或链接都表示赞赏。
答案 0 :(得分:6)
您不应为所有内容创建单独的容器,而应使用单个容器。您可以创建一个容器“Extension”,它基本上只是在容器上设置服务。
以下是我建议的设置的大量示例。当你有一个容器时,递归地解析依赖关系是微不足道的。如您所见,security.authentication_provider
取决于db
,这取决于db.config
。
由于闭包的懒惰,很容易定义服务,然后在以后定义它们的配置。此外,您可以轻松覆盖它们,正如您可以在TestExtension中看到的那样。
可以将服务定义拆分为多个单独的扩展,以使其更具可重用性。这几乎是Silex microframework所做的(它使用了疙瘩)。
我希望这能回答你的问题。
<强> ExtensionInterface 强>
class ExtensionInterface
{
function register(Pimple $container);
}
<强> AppExtension 强>
class AppExtension
{
public function register(Pimple $container)
{
$container['mailer'] = $container->share(function ($container) {
return new Mailer($container['mailer.config']);
});
$container['db'] = $container->share(function ($container) {
return new DatabaseConnection($container['db.config']);
});
$container['security.authentication_provider'] = $container->share(function ($container) {
return new DatabaseAuthenticationProvider($container['db']);
});
}
}
<强> ConfigExtension 强>
class ConfigExtension
{
private $config;
public function __construct($configFile)
{
$this->config = json_decode(file_get_contents($configFile), true);
}
public function register(Pimple $container)
{
foreach ($this->config as $name => $value) {
$container[$name] = $container->protect($value);
}
}
}
<强> config.json 强>
{
"mailer.config": {
"username": "something",
"password": "secret",
"method": "smtp"
},
"db.config": {
"username": "root",
"password": "secret"
}
}
<强> TestExtension 强>
class TestExtension
{
public function register(Pimple $container)
{
$container['mailer'] = function () {
return new MockMailer();
};
}
}
<强> ContainerFactory通过强>
class ContainerFactory
{
public function create($configDir)
{
$container = new Pimple();
$extension = new AppExtension();
$extension->register($container);
$extension = new ConfigExtension($configDir.'/config.json');
$extension->register($container);
return $container;
}
}
<强>应用强>
$factory = new ContainerFactory();
$container = $factory->create();
测试Bootstrap
$factory = new ContainerFactory();
$container = $factory->create();
$extension = new TestExtension();
$extension->register($container);