【问题标题】:Searching for an elegant way in PHP for loading dependencies/services/configuration?在 PHP 中寻找一种优雅的方式来加载依赖项/服务/配置?
【发布时间】:2011-11-08 21:31:55
【问题描述】:

我正在构建一个 MVC PHP 框架,我想知道哪些是在我的类中加载我需要的内容的最佳实践,无论是其他类还是普通配置。

直到今天,我一直在使用单例、注册表和最近的依赖注入容器。 虽然许多人声称 DI 是必经之路,但在我看来,它只是将组件之间的耦合问题转移到了另一个地方。

Singleton 引入了全局状态,registry 引入了紧密耦合,而 DI 引入了......嗯,很多复杂性。我仍然很困惑,找不到正确的方法来连接我的课程。

与此同时,我想出了一个自定义解决方案。其实它不是一个解决方案,它只是从我的代码中抽象出服务加载的实现。

我使用 _load_service 和 _load_config 方法构建了一个抽象类,我的框架的所有组件都扩展了这些方法以加载其他服务或配置。

abstract class Base_Component {
    function _load_service($service) {
        // could be either
        return DI_container::getInstance()->$service;

        // or
        $class = '\services\\'.$service;
        return new $class;

        // or other implementation
    }
}

加载它们的实现现在只在一个地方实现,基类,所以至少我在我的组件中去掉了如下代码行:

$database = new Database(Registry::getInstance()->load('db_config'));

$database = DI_container::getInstance()->database;

现在如果想要一个数据库实例,我会这样做

$database = $this->_load_service('database');

以及服务加载器、容器、注册表或任何可以在单个类方法中轻松更改的实现,而无需搜索我的所有代码来更改对我之前使用的任何容器实现的调用。

但正如我所说,我什至不确定我将使用什么方法来加载类和配置。

你有什么意见?

【问题讨论】:

  • 我编辑了答案以添加代码示例

标签: php oop dependency-injection serviceloader


【解决方案1】:

为什么要重新发明轮子?使用Pimple 作为您的 DI 容器,并从其文档中了解如何使用它。

或者,使用Silex 微框架作为基础来创建您自己的框架。它扩展了 Pimple 功能,因此您可以使用依赖注入。

要回答您的问题,这是您在不将类与其耦合的情况下使用 DI 的方式:

interface ContainerInterface {
    public function getService($service_name);
    public function registerService($service_name,Closure $service_definition);
}

class Application {
    public function __construct(ContainerInterface $container) {
        $this->container= $container;
    }

    public function run() {
        // very simple to use!
        $this->container->getService('db')->someDatabaseQuery();
    }
}

$c = new My_DI_Container;

// Service definitions could be in a separate file
$c->registerService('db',function() { return new Database('some config'); });

// Then you inject your DI container into the objects that need it
$app = new Application($c);
$app->run(); // or whatever

这样,DI 容器就解耦了,将来您可以使用不同的实现。唯一的要求是它实现了 ContainerInterface。

请注意,容器对象是被推送的,而不是被拉出的。避免使用单例。要获取/设置单实例对象,请使用容器(这是它的职责)。要获取容器实例,只需通过构造函数将其推送。

【讨论】:

  • 我正在“重新发明轮子”以从中获得知识和开发经验。否则,当有这么多可用的优秀框架时,我不会费心制作一个框架。我研究了疙瘩,并建立了类似但更简单的东西。
  • 但是在构建 DI 容器之前,您应该了解为什么以及如何使用它。而且您似乎一直在通过静态调用引入隐式依赖项(这与您在使用 DI 时想要的相反)。您还提到 DI 引入了复杂性。这就是 DI 容器的用途:让 DI 变得简单
  • 与我目前所做的相比,这似乎是一种更简洁的方法。已接受答案。
  • 这不是依赖注入,这是一个名为“容器”的服务定位器......-.-
  • @ChristofferBubach 那么什么是依赖注入?你能举一个简单的 DI 例子吗,就像 HappyDeveloper 对“容器”所做的那样?
【解决方案2】:

回答您的问题;查看 PHP autoloading。通过自动加载注册类,这样您就不必在任何地方放置 require/includes,这对 RAD(快速应用程序开发)确实有积极影响。

我的想法:

感谢您尝试如此艰巨的任务,您的方法似乎基于单身和工厂等良好实践。

我不关心依赖注入。 OOP 基于封装,将一个对象注入另一个对象,imo 打破了这种封装。当您将一个对象注入另一个对象时,目标对象必须“相信”注入的对象没有任何变化,否则您可能会出现异常行为。

考虑为你的类命名空间(不是 PHP 命名空间,而是像 Zend 那样为你的框架添加前缀,Zend_),这将有助于你注册一个命名空间,然后当一个类被调用时,自动加载器将确保正确类已加载。这就是 Zend_Framework 的工作原理。详情请查看Zend_Loader_Autoloader。 Symfony 框架实际上更进一步。在第一个请求期间,它将遍历所有已知位置以查找类文件,然后构建一个类数组和文件路径,然后将数组保存到文件中(文件缓存),因此后续请求不会相同的开销。您的框架需要考虑的事项。

就配置文件而言,Symfony 使用 YAML 文件,我发现它非常灵活。您甚至可以包含 PHP 代码以提高灵活性。 Symfony 提供了一个易于使用的独立的YAML parser。您可以通过添加缓存层和缓存解析的 YAML 文件来提高性能,这样您就不必为每个请求解析文件。

我假设您是在 ORM 之上构建框架。我的建议是不要针对特定​​于 ORM 版本的任何功能,否则您的框架会与该版本耦合,您将不得不同时升级 ORM 和框架。

我建议您深入了解其他框架,看看您是否可以从中挑选出最好的;从而形成一个可靠、易于使用的框架。

【讨论】:

  • 感谢您的贡献,但您实际上并没有回答我的问题。这是我的错,我忘了提到我已经使用了命名空间和类自动加载。我的问题不是如何只加载类,而是如何在不引入它们之间的紧密耦合和难以维护的代码的情况下加载它们。
猜你喜欢
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多