问题描述
|
使用pimple作为我的DI容器,我一直在勇敢地重构小类以依赖于DI注入,从而消除了我可以轻易删除的硬编码依赖。
我完成此任务的方法很简单,但是我不知道是否合适,因为除了上个月在这里学到的知识之外,我对DI和单元测试的经验还很少。
我创建了一个类ContainerContainerFactory,该类是pimple的子类,并且在该子类中创建了一些方法,这些方法仅返回特定对象的容器。
构造函数根据类型调用适当的creator方法:
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
并在其中进行覆盖,而不是将测试代码与应用程序代码混合,并使用$ 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现在需要将容器传递给它。
我意识到我的英语水平不是很好,希望我能讲得足够好,让其他SO''er理解。
我的问题有两个方面,你们如何以一种简单的方式处理为本身包含依赖关系的依赖关系创建对象?
并且..如何将容器配置分开以创建对象以进行测试?
与往常一样,任何评论或链接到相关文章表示赞赏。
解决方法
您不应为所有内容创建单独的容器,而应使用单个容器。您可以创建一个容器“ Extension”,该容器基本上只是在容器上设置服务。
这是我建议的设置的广泛示例。当您只有一个容器时,递归地解决依赖关系是微不足道的。如您所见,
security.authentication_provider
取决于db
,which5 on取决于which6ѭ。
由于闭包的惰性,很容易定义服务,然后再定义它们的配置。此外,您也可以很容易地覆盖它们,就像使用TestExtension所看到的那样。
可以将服务定义分为多个单独的扩展,以使其更可重用。这几乎就是Silex微框架所做的(它使用粉刺)。
我希望这回答了你的问题。
扩展接口
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\"
}
}
测试扩展
class TestExtension
{
public function register(Pimple $container)
{
$container[\'mailer\'] = function () {
return new MockMailer();
};
}
}
集装箱工厂
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();
测试引导程序
$factory = new ContainerFactory();
$container = $factory->create();
$extension = new TestExtension();
$extension->register($container);