【问题标题】:DI Container - correct way of doing it?DI Container - 正确的做法?
【发布时间】:2015-04-09 07:24:45
【问题描述】:

我有一长串依赖注入来显示带有文章、导航等的页面。目前我将它们放在一个名为 index.php 的文件中以将它们粘合在一起。

index.php,

use MyCustomVerndorName\Constant\Mapper\ConstantsMapper;
use MyCustomVerndorName\Slug\Mapper\SlugMapper;
.... (more)

$ConstantService = new ConstantService();
$ConstantController = new ConstantController();

$ArticleService = new ArticleService();
$ArticleController = new ArticleController();

// Prepare Nav model.
$NavModel = new NavModel();
$NavMapper = new NavMapper($PdoAdapter);
$NavService->setMapper($NavMapper)->setModel($NavModel);

// Prepare Article model.
$ArticleModel = new ArticleModel();
$ArticleMapper = new ArticleMapper($PdoAdapter);

// Prepare components.
$ArticleContentComponent = new ArticleContentComponent($PdoAdapter);
... (more)

// Inject components.
$ArticleMapper->addComponent($ArticleContentComponent);
... (more)

$NavChildrenComponent = new NavChildrenComponent($PdoAdapter);
... (more)

// Inject components.
$NavMapper->addComponent($NavChildrenComponent);
$NavMapper->addComponent($NavLanguageComponent);

// Controll the slug.
$SlugController->setService($SlugService)->fetchRow([
    "url"   =>  $url
]);

// Control the nav.
$NavController->setService($NavService)->fetchRows();

// Controll the article.
$ArticleService->setMapper($ArticleMapper)->setModel($ArticleModel);
$ArticleController->setService($ArticleService)->fetchRow([
    "url"   =>  $url
]);

// Prepare template.
$PageTemplate = new PageTemplate();

// Prepare view.
$ArticleView = new ArticleView($PageTemplate, $ArticleModel);
$ArticleView->setSlug($SlugModel);
$ArticleView->setNav($NavModel);

// Render the view.
echo $ArticleView->render('index.php');

在我的 router.php 中(我正在使用 AltoRouter),

use AltoRouter as Router;

$Router = new Router();.
$Router->map('GET', '/', '/article/container');
... (and other routes)

if($match)
{
    $target = $match['target'];
    $url = isset($match['params']['url']) ? $match['params']['url'] : DEFAULT_HOMEPAGE;
    $language = isset($match['params']['language']) ? $match['params']['language'] : null;

    include __DIR__ . $target . '.php';

}

我正在考虑将 index.php 中的依赖注入列表制作成一个容器类,这样我就可以在需要时调用这个类,

对于 router.php 中的实例,

$Router->map('GET', '/', 'MyCustomVerndorName\Article\Container\ArticleContainer');
....
new $target($PdoAdapter, $url, $language);

还有这个容器类,

namespace MyCustomVerndorName\Article\Container;

// Mapper.
use MyCustomVerndorName\Constant\Mapper\ConstantsMapper;
...

class ArticleContainer
{

    /*
     * Construct dependency.
     */ 
    public function __construct(\MyCustomVerndorName\Adapter\PdoAdapter $PdoAdapter, $url, $language)
    {
        $ConstantService = new ConstantService();
        $ConstantController = new ConstantController();

        // Slug.
        $SlugService = new SlugService();
        $SlugController = new SlugController();

        // Nav.
        $NavService = new NavService();
        $NavController = new NavController();

        // Article.
        $ArticleService = new ArticleService();
        $ArticleController = new ArticleController();

        // Prepare Article model.
        $ArticleModel = new ArticleModel();
        $ArticleMapper = new ArticleMapper($PdoAdapter);

        // Prepare components.
        $ArticleContentComponent = new ArticleContentComponent($PdoAdapter);
        ...

        // Inject components.
        $ArticleMapper->addComponent($ArticleContentComponent);
        ...

        // Control the nav.
        $NavController->setService($NavService)->fetchRows();

        // Controll the article.
        $ArticleService->setMapper($ArticleMapper)->setModel($ArticleModel);
        $ArticleController->setService($ArticleService)->fetchRow([
            "url"   =>  $url
        ]);


        // Prepare template.
        $PageTemplate = new PageTemplate();

        // Prepare view.
        $ArticleView = new ArticleView($PageTemplate, $ArticleModel);
        $ArticleView->setSlug($SlugModel);
        $ArticleView->setNav($NavModel);

        // Render the view.
        echo $ArticleView->render('index.php');
    }
}

基本上,我把所有的依赖列表放到__construct

public function __construct(\MyCustomVerndorName\Adapter\PdoAdapter $PdoAdapter, $url, $language)
{
...
}

那么,在不依赖 PHP 的知名容器(例如 Pimple、Symfony\DependencyInjection 等)的情况下,这是正确的方法 DI 容器吗?

【问题讨论】:

  • 似乎不能很好地扩展。我可以向您展示一个基于闭包的解决方案,其中包含更通用的容器。
  • 谢谢 Cerad,是的,请告诉我基于闭包的解决方案...

标签: php symfony inversion-of-control ioc-container


【解决方案1】:

传统上,DI 容器根据需要返回服务。您所调用的 ArticleContainer 实际上只是一个函数,然后呈现一个视图。它做了很多 DI 注入,但它不是一个容器。

假设您有一个真实的容器,所有容器都已正确初始化。您的代码如下所示:

$container = new Container();

// Some initialization

// Now use it
$viewArticle = $container->get('view_article');
echo $viewArticle->render('index.php');

希望您能看到不同之处。初始化完成后,您的应用程序只需拉出它需要的视图并呈现它。无需担心 pdo 或 url 等。

那么我们如何将我们的文章查看服务放入容器中呢?

$container = new Container();

// Some initialization
$container->set('page_template',function($container)
{
  return new PageTemplate();
}
$container->set('article_view',function($container)
{
  $articleView = new ArticleView(
    $container->get('page_template', 
    $container->get('article_model')
  );
  $articleView->setSlug($container->get('slug_model');
  $articleView->setNav ($container->get('nav_model');

  return $articleView;
};
// Now use it
$viewArticle = $container->get('article_view');
echo $viewArticle->render('index.php');

所以我们为我们的容器定义了两个服务函数。当我们执行 $container->get('article_view'); 时,容器以容器本身作为参数调用该函数。该函数通过将依赖项拉出容器来创建所需的类,然后返回新对象。

假设您的应用程序中可能会有更多的页面浏览量,并且其中大多数都需要一个 page_template。我们定义的相同 page_template 服务也可以被您的其他视图使用。所以我们开始从容器中获得一些重用。

您基本上只是继续向容器添加服务。

您可以为此使用 Pimple,但制作自己的容器也很有指导意义。真的不多。

【讨论】:

  • 感谢 Cerad 的回答。因此,如果您有 100 个由容器管理的类,则需要将所有 100 个类注册到容器中。那么我在 index.php 和容器中将它们粘合在一起的方式有什么区别?将它们粘贴到 index.php 中似乎更容易和直观,不是吗?
  • 部分差异来自共享服务定义。如果您的页面模板在多个视图中使用,那么您只需定义一次。此外,如果您需要更改类的构造函数,则只需更改服务功能,所有用户都将获得新定义。能够提取单个服务进行测试也很方便。但是你有什么作品,所以如果你愿意,那就去吧。它不是 DI 容器,但没关系。
  • 知道了。我现在明白了。非常感谢您的回答和回复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-16
  • 2013-10-27
  • 1970-01-01
  • 2015-08-12
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
相关资源
最近更新 更多