2

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

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

Singletons 引入了全局状态,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');

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

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

你有什么意见?

4

2 回答 2

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。

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

于 2011-11-09T17:30:59.223 回答
0

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

我的想法:

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

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

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

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

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

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

于 2011-11-08T21:48:59.087 回答