我有一个小框架,我这样编码。我不确定它是否被称为依赖注入。我不知道它是否像设计模式。我也不知道并想知道将$this
作为参数传递是一种不好的做法。
看看这个; (不是一个有效的例子,只是将这些代码写入浏览器进行解释。)
/* This is engine model */
require_once('Database.class.php');
require_once('Image.class.php');
require_once('Misc.class.php');
require_once('BBCode.class.php');
class FrameWork_Engine_Model
{
public $database, $config, $misc, $bbcode, $controller, $image;
function __construct($config)
{
$this->database = new Database($configParams);
$this->image = new Image($this);
$this->misc = new Misc($this);
$this->bbcode = new BBCode($this);
$this->controller = new Controller($this); //here I call Register controller depending on routing, in this case, register controller.
}
...
}
/* This is register controller */
class Register extends Base_Controller
{
/*I can access anything over Engine Model in my controllers */
$this->engine->database->query(); //I access database model
$this->engine->bbcode->tag('you'); //I access bbcode model
$this->engine->image->sanitizeUploadedFile(); //I access image model
//etc. I can access others models like this.
}
基本上,我的控制器可以通过引擎模型访问任何模型。我相信dependency injection is all about injecting dependencies into controllers?
就像,我的注册控制器需要数据库模型,路由模型和模板模型才能工作。这里有它依赖的一切。我错了吗?
有了这些说法,我的问题是:
它是一个有效的依赖注入示例吗?如果不是,它是什么?它在设计模式中是否有名称?
如果它与依赖注入无关,那么DI需要做哪些更改?
在新创建的类上传递$this
参数是不是很糟糕?如果是这样,为什么?
聚苯乙烯。我知道在一个主题中问3个问题不是stackoverflow喜欢的东西,但我不想复制粘贴整个文本来问他们。
答案 0 :(得分:19)
你快到了。
不,我不认为它是一个有效的依赖注入示例。它类似于服务定位器(因为您将整个容器注入到服务中并使用它来“定位”相关服务)。
您在依赖注入和依赖注入容器之间存在一点混淆。
首先,依赖注入意味着在运行时将依赖项推送到对象中,而不是创建/拉取它们。
举例说明:
//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
或者OptionalInterface的“协议”的依赖关系“连线”。 $dep2
(因此Inversion of Control的名称 - 依赖注入背后的基本概念)。
执行此连线的组件称为dependency injection container。
现在,一个dependency injection container,最简单的形式,只不过是一个能够在运行时基于某种形式的配置连接对象的对象。
我说你差不多了,但是你的实施存在一些问题:
$this
)作为依赖项传递,因为这样您就会回退到较弱的inversion of control,即service locator。您应该将具体的依赖项传递给服务构造函数在某些情况下,您会发现自己想要将整个$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()
有什么可以改进的?你的容器应该:
所有这些都附有关于Dependency Injection
的明确文档答案 1 :(得分:6)
FrameWork_Engine_Model
是注册表(Registry Pattern)。将注册表作为依赖项注入所有对象是一种误解的依赖注入。从技术上讲,是 DI,但是您可以创建从一切到的依赖,并且还会消除DI应该提供的灵活性。FrameWork_Engine_Model
旨在实例化服务并管理其依赖关系,则可以将其更改为Inversion of Control Container(与DI相关的典型模式)我不会争论你选择的班级名称以及你的服务和管理员的职责,因为我认为这不属于这个问题的范围。只是一句话:看起来你的控制器做得太多了。如果您对干净的代码感兴趣,可能需要查看Single Responsibility Principle并保持控制器“瘦”,将业务逻辑和数据库查询移动到服务层,并将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的全局变量。