在PHP web应用程序中使用依赖项注入
我最近读了很多关于依赖注入的书,从理论上讲,它很有意义。拥有易于测试的单一责任类的想法听起来很聪明。然而,我正挣扎着要走多远 我现在正在开发一个PHPWeb应用程序,它有一个CMS组件,并且是数据库驱动的。在CMS中,您可以创建页面,页面本身是使用各种小部件构建的。这些小部件可以是直接的HTML(来自WYSIWYG编辑器),也可以稍微复杂一些,比如照片库。这是一个重要的部分,照片库小部件依赖于其他数据库模型,如在PHP web应用程序中使用依赖项注入,php,dependency-injection,Php,Dependency Injection,我最近读了很多关于依赖注入的书,从理论上讲,它很有意义。拥有易于测试的单一责任类的想法听起来很聪明。然而,我正挣扎着要走多远 我现在正在开发一个PHPWeb应用程序,它有一个CMS组件,并且是数据库驱动的。在CMS中,您可以创建页面,页面本身是使用各种小部件构建的。这些小部件可以是直接的HTML(来自WYSIWYG编辑器),也可以稍微复杂一些,比如照片库。这是一个重要的部分,照片库小部件依赖于其他数据库模型,如PhotoGallery和photogalleryimage,以便正常工作 根据,我不
PhotoGallery
和photogalleryimage
,以便正常工作
根据,我不应该通过应用程序的不同层传递对依赖项的引用。相反,每个类应该简单地“请求”它所需要的任何依赖项。以他的例子来看,情况如下:
$db = new Database()
$repo = new UserRepository($db);
$page = new LoginPage($repo);
虽然我可以看到这在我的应用程序的服务层中工作得很好,但我真的不明白这在我的值对象/实体中是如何工作的。在我看来,这种模式要求我在应用程序的最高层实例化所有模型。然而,当我需要我的页面类来告诉我需要实例化哪些小部件时,如何在该级别实例化我的所有小部件?在不知道我正在创建哪些小部件的情况下,我如何知道需要创建和注入哪些小部件依赖关系
理论上,DI对我来说很有意义。然而,事实证明,在我的新web应用程序中实现这种模式更加困难。关于DI和IoC,我还缺少什么吗?谢谢
更新:对Tandu的回应
非常感谢Tandu的回复。我认为DI实际上可能不是我的问题,因为我认为我非常清楚它是什么,无论它是构造函数还是setter注入,或者它是使用容器还是穷人的方式完成的。我认为正在发生的是,DI正在挑战我重新思考我目前的编程方式。我认为我的OOP设计就是问题所在。为了说明这一点,我通常会对这个问题进行编程:
class Page
{
public $id;
public $name;
public function widgets()
{
// Perform SQL query to select all widgets
// for this page from the database, and then
// return them as Widget objects.
}
}
interface Widget
{
public $id;
public $page_id;
public $type;
public function widgetize();
}
class PhotoGallery_Widget implements Widget
{
public $id;
public $page_id;
public $type;
public function widgetize()
{
$gallery = new Photogallery();
foreach($gallery->images())
{
// Create gallery HTML code
}
}
}
// Finally, to create the page, I would:
$page = new Page(1);
foreach($page->widgets() as $widget)
{
$widget->widgetize();
}
显然这里有一些问题。页面
模型取决于小部件
模型。PhotoGallery\u小部件
模型取决于PhotoGallery
模型,甚至PhotoGallery
模型也取决于photogalleryimage
模型。问题是,这种发展方式对我很有效。随着我越来越深入到应用程序的各个层,每一层都承担着创建所需对象的责任。引入DI对我的思维方式产生了重大影响。然而,我想学习如何正确地做到这一点。就因为这对我来说很有效,并不意味着我应该继续这样做
你提到使用工厂。但是工厂实际上不是因为实例化对象而破坏DI吗
您可能还注意到,我的模型负责与数据库交互。我怀疑这也是我的设计中的一个问题。我是否应该使用某种类型的数据映射来消除模型中的这种责任
鉴于这个关于我目前如何实现代码的更具体的例子,您还有其他建议吗,或者您能进一步说明我应该如何以不同的方式实现吗?不看具体的实现,很难给出具体的建议,因此我将提供我可以提供的一般建议(事实上,我不是很好)。听起来你可以使用一个“依赖注入容器”(google It..找到一个你喜欢的容器,或者为你自己的目的滚动)来管理类的大型互锁依赖关系 您应该记住的一件事是,您的代码应该轻轻地落在设计周围,这将非常有力地支撑它。您不应该努力让代码遵循您创建的设计。如果您试图做的事情不符合您对设计的理解,那么可能是时候进行重写了 您也只使用构造函数注入的例子,我相信您知道在适当的时候可以使用其他类型的注入,比如setter注入 正如我所说,我没有看到你的代码,但是你的设计的问题是,它不是应该知道它需要什么小部件的页面,而是服务。如果你将特定的小部件实现绑定到一个页面,这违反了依赖倒置(并使依赖注入更加困难)。相反,您的代码应该更符合以下内容:
interface Widget {
function widgetize();
}
class ConcretePage {
private $widgets = array();
public function addWidget(Widget $widget) {
$this->widgets[] = $widget;
}
public function run() {
foreach ($this->widgets as $widget) {
$widget->widgetize();
}
}
}
该服务将创建一个页面并添加所需的小部件。该页面不应该关心这些小部件。您可能还需要在页面和小部件之间实现一个桥梁。该服务也可以处理这一点
正如您提到的文章作者所说,您应该有专门用于构建整个对象图的代码(这是所有新
操作符的所在)。想象一下,如果没有这些小部件的类定义,测试需要五个特定小部件的页面会有多困难!您需要依赖抽象
这可能不符合DI的精神,但更简单的解决方案是使用WidgetFactory
:
class Page {
private $wf;
private $widgets = array('whiz', 'bang');
public function __construct(WidgetFactory $wf) {
$this->wf = $wf;
}
public function widgetize() {
foreach ($this->widgets as $widget) {
$widget = $this->wf::build($widget);
}
}
}
WidgetFactory
接口可以从应用程序到应用程序,甚至在测试中都是独立的。这仍然可以为窗口小部件页面提供某种程度的抽象。对我来说,依赖注入只会有用,因为我可以在语法$This->method()或$obj->method()之间进行选择;调用相同的方法。这可以通过使用神奇的函数set、get和call来完成。很抱歉,我花了这么长时间才做出响应,但我不得不花了很多时间