Php 这是依赖注入吗?这是一种糟糕的做法吗?

Php 这是依赖注入吗?这是一种糟糕的做法吗?,php,oop,dependency-injection,Php,Oop,Dependency Injection,我有一个小框架,我这样编码。我不确定它是否被称为依赖注入。我不知道它是否像一个设计模式。我也不知道,也不知道作为param传递$this是否是一种不好的做法 看看这个,;(不是一个有效的示例,只是将这些代码写入浏览器中进行解释。) 基本上,我的控制器可以通过引擎模型访问任何模型。我相信依赖注入就是将依赖注入控制器?就像,我的注册控制器需要一个数据库模型、路由模型和模板模型来工作。这里有它所依赖的一切。我弄错了吗 话虽如此,我的问题是: 这是一个有效的依赖注入示例吗?如果没有,是什么?它在设计模

我有一个小框架,我这样编码。我不确定它是否被称为依赖注入。我不知道它是否像一个设计模式。我也不知道,也不知道作为param传递
$this
是否是一种不好的做法

看看这个,;(不是一个有效的示例,只是将这些代码写入浏览器中进行解释。)


基本上,我的控制器可以通过引擎模型访问任何模型。我相信依赖注入就是将依赖注入控制器?就像,我的注册控制器需要一个数据库模型、路由模型和模板模型来工作。这里有它所依赖的一切。我弄错了吗

话虽如此,我的问题是:

  • 这是一个有效的依赖注入示例吗?如果没有,是什么?它在设计模式中有名字吗

  • 如果它与依赖注入无关,那么需要做哪些更改才能进行DI

  • 在新创建的类上传递
    $this
    参数是一种糟糕的做法吗?若然,原因为何

  • 另外,我知道stackoverflow不喜欢在一个主题中问3个问题,但我不想复制粘贴整个文本来提问

  • 您的
    FrameWork\u Engine\u模型是一个注册表()。将注册表作为依赖项注入所有对象是一种误解的依赖项注入。从技术上讲,它是DI,但是您创建了一个从所有事物到所有事物的依赖关系,并且剥夺了DI应该提供的灵活性
  • 如果您的
    FrameWork\u Engine\u模型
    旨在实例化服务并管理它们的依赖关系,您可以将其更改为(与DI相关的典型模式)
  • 不,不是一般的
  • 我不会争论您对类名的选择以及服务和控制器的责任,因为我认为这不在这个问题的范围之内。只是一句话:看起来你的控制器做得太多了。如果您对干净的代码感兴趣,您可能希望查看并保持控制器“精简”,将业务逻辑和数据库查询移动到服务层,并将bbcode等输出机制输出到视图

    那么,回到您的示例,以及如何将其更改为依赖项注入的合理用法。基本IoC容器可以如下所示:

    public function createRegisterController()
    {
        $controller = new RegisterController();
        $controller->setImage($this->getImageService());
        // ...
        return $controller;
    }
    public function getImageService()
    {
        if ($this->imageService === null) {
            $this->imageService = new Image();
            // inject dependencies of Image here
        }
        return $this->imageService;
    }
    
    这里重要的一点是:只注入所需的依赖项。不要创建一堆伪装成DI的全局变量。

    您就快到了

    问题1 不,我不认为它是一个有效的依赖注入示例。它有点像服务定位器(因为您将整个容器注入到您的服务中,并使用它来“定位”依赖的服务)

    问题2 您在依赖项注入和依赖项注入容器之间制造了一个小的混淆

    首先,依赖项注入意味着在运行时将依赖项推送到对象中,而不是创建/拉取它们

    举例来说:

    //hardcoded dependecies
    class BadService
    {
        public function __construct() 
        {
            $this->dep1 = new ConcreteObject1();
            $this->dep2 = new ConcreteObject2();
        }
    }
    
    因此,在上面的示例中,
    BadService
    使得在运行时连接其他依赖项变得不可能,因为它们已经被硬拉入构造函数本身

    //service locator pattern
    class AlmostGoodService
    {
        public function __construct(Container $container)
        {
            $this->dep1 = $container->getADep1();
            $this->dep2 = $container->getADep2();
        }
    }
    
    AlmostGoodService
    示例中,我们已经从前面的示例中删除了硬依赖项,但是我们仍然依赖于容器的特定实现(这意味着如果不为该容器提供实现,我们的服务是不可重用的)。这是一个与您正在做的事情相匹配的示例

    //dependecy injection    
    class GoodService
    {
        public function __construct($dep1, OptionalInterface $dep2)
        {
            $this->dep1 = $dep1;
            $this->dep2 = $dep2;
        }
    }
    
    GoodService
    服务不涉及其具体依赖项的创建,并且可以在运行时轻松地与实现
    $dep1
    的“协议”或
    $dep2
    的可选接口的任何依赖项“连接”(因此,名称-依赖项注入背后的基本概念).

    进行此接线的部件称为a

    现在,以最简单的形式,a只不过是一个能够在运行时基于某种形式的配置连接对象的对象

    我说过,您已经快到了,但您的实施存在一些问题:

    /* This is the revised engine model */
    class FrameWork_Engine_Model
    {
        function __construct($config)
        {
                $this->config = $cofig; 
        }
    
        public function database()
        {
            require_once('Database.class.php');
            return new Database($this->config['configParams']);
        }
    
        public function bbcode()
        {
            require_once('BBCode.class.php');
            return new BBCode($this->database());
        }
    
        public function image()
        {
            require_once('Image.class.php');
            $this->image = new Image($this->config['extensionName']);
        }
        ....
    
        public function register_controller($shared = true)
        {
            if ($shared && $this->register_controller) {
              return $this->register_controller;
            }
    
            return $this->register_controller = new Register_Controller($this->database(), $thus->image(), $this->bbcode());
        }
     }
    
    • 连接应该是惰性的(您不想让所有这些都在构造函数中工作,因为随着应用程序的增长,它会大大降低速度)
    • 您不应该将整个容器(
      $this
      )作为依赖项传递,因为这样您就会退回到较弱的容器,即。相反,您应该将具体的依赖项传递给您的服务构造函数
    问题3 在某些情况下,您会发现自己希望将整个
    $container
    作为依赖项传递给服务(即控制器或惰性服务工厂),但通常最好不要使用这种做法,因为它将使您的服务更易于重用和测试。当您觉得您的服务有太多依赖项时,这是一个好迹象,表明您的服务做得太多,现在是拆分它的好时机

    原型容器实现 因此,根据我上面的回答,这里是一个经过修改的(远不完美的)实现:

    /* This is the revised engine model */
    class FrameWork_Engine_Model
    {
        function __construct($config)
        {
                $this->config = $cofig; 
        }
    
        public function database()
        {
            require_once('Database.class.php');
            return new Database($this->config['configParams']);
        }
    
        public function bbcode()
        {
            require_once('BBCode.class.php');
            return new BBCode($this->database());
        }
    
        public function image()
        {
            require_once('Image.class.php');
            $this->image = new Image($this->config['extensionName']);
        }
        ....
    
        public function register_controller($shared = true)
        {
            if ($shared && $this->register_controller) {
              return $this->register_controller;
            }
    
            return $this->register_controller = new Register_Controller($this->database(), $thus->image(), $this->bbcode());
        }
     }
    
    现在,要使用您的服务:

    $container = new FrameWork_Engine_Model(); 
    $container->register_controller()->doSomeAction()
    
    还有什么可以改进的?您的容器应:

    • 提供一种共享服务的方式,即只初始化一次服务
    • be-提供一种在配置后锁定它的方法
    • 能够与其他容器“合并”——这样您的应用程序将真正模块化
    • 容许
    • 容许
    • 支持
    随时可用的DI容器实现 所有这些都附有关于

    • -PHP5.3轻量级DI容器
    • -PHP5.3功能完整DI容器
    • -小型PHP 5.2 DI容器

    +1对于指出DI和DI/IoC容器之间的区别,回答得很好。非常感谢!谢谢,我会记住的。虽然我
    $container = new FrameWork_Engine_Model(); 
    $container->register_controller()->doSomeAction()