看看这个; (不是一个工作的例子,只是将这些代码写入浏览器进行说明.)
/* 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. }
基本上,我的控制器可以通过引擎模型访问任何型号.我认为依赖注入是关于将依赖关系注入控制器?像我的注册控制器需要一个数据库模型,路由模型和模板模型工作.这里有它所依赖的一切.我错了吗
有了这些话,我的问题是:
>这是一个有效的依赖注入示例吗?如果不是,那是什么?它有设计模式的名称吗?
>如果与依赖注入无关,DI需要做哪些改变?
在新创建的类上传递$this参数是不好的做法?如果是,为什么?
PS.我知道问题中的3个问题不是stackoverflow喜欢的东西,但是我不想复制粘贴整个文本来询问它们.
问题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的OptionalInterface的“协议”的任何依赖关系(因此名称为Inversion of Control – 依赖注入背后的基本概念).
执行此接线的组件称为dependency injection container.
现在,dependency injection container是最简单的形式,只不过是一种能够在运行时基于某种形式配置对象的对象.
我说你几乎在那里,但你的实现有一些问题:
>接线应该是懒惰的(你不想让所有的工作在你的构造函数中,因为你的应用程序会随着增长而大大减慢)
>你不应该将整个容器($this)作为一个依赖关系,因为你会回到一个较弱的inversion of control,即service locator.你应该把具体的依赖关系传递给你的服务构造函数
问题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()
可以改进什么?您的容器应该:
>提供一种分享服务的方式 – 也就是仅将其初始化一次
> lockable – 提供一种在配置后锁定它的方法
>能够与其他容器“合并” – 这样您的应用程序将真正模块化
>允许optional dependencies
>允许scopes
>支持tagging services
准备使用DI容器实现
所有这些都有关于Dependency Injection的明确文件
> Pimple – PHP 5.3轻量级DI容器
> Symfony2 DI Container – PHP 5.3功能完整的DI容器
> Juice DI – 小PHP 5.2 DI容器