现代PHP框架(如Zend,Symfony,Phalcon)都使用DI容器,您只需传递它以访问所有框架功能。我想知道我是否应该/可以在我的业务代码中使用DI容器。让我们说我需要使用数据库访问对象和邮件程序对象,因为框架使用它们,它们已经在DI中。我可以在实例化业务类时简单地传递DI容器吗?
例如,我有一个处理数据库中用户的类,您可以将其称为我的用户模型类。现在,我只需将DI容器传递给模型类的构造函数,然后在控制器中实例化它,并且它很简单。只需将所有东西都放在DI容器中即可。
但是我即将开发一个也将使用此用户模型类的API。由于它需要一个DI容器,我需要事先知道模型的依赖关系,并用正确的容器初始化DI容器。之前,我只是将每个依赖项作为参数传递给构造函数,但是我需要知道IoC,而不需要查看参数,类的依赖性以及用于访问每个依赖项的名称是什么。例如,我需要知道应该有一个由' db'标识的PDO对象。在DI容器中。这是商业/图书馆代码的好方法吗?
我可能在这里混淆了条款,但我希望你明白这一点。
谢谢!
答案 0 :(得分:1)
您正在开发什么样的代码,无论是业务逻辑还是框架逻辑(或其他类型的逻辑),这并不重要,这里的重点是如何处理类依赖。
术语业务逻辑本身非常抽象。您可以使用单个类(a.k.a domain object)表示业务逻辑,也可以将业务逻辑表示为layer。
开发任何应用程序时,应记住可以更改数据库(或者将来可以迁移到NoSQL解决方案)。如果你这样做,那么$pdo
不再是依赖。如果您的存储逻辑完全与业务逻辑分离,那么替换存储引擎将非常容易。否则,在更改它时,你最终会重写很多东西。
正确设计的架构鼓励将存储逻辑与应用程序逻辑分离。这些模式是最佳实践:数据映射器或表网关
namespace Storage\MySQL;
use PDO;
abstract class AbstractMapper
{
protected $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
}
class UserMapper extends AbstractMapper
{
private $table = 'cms_users';
public function fetchById($id)
{
$query = sprintf('SELECT * FROM `%s` WHERE `id` =:id', $this->table);
$stmt = $this->pdo->prepare($query);
$stmt->execute(array(
':id' => $id
));
return $stmt->fetch();
}
// the rest methods that abstract table queries
}
因此,在这种情况下,对于当前存储引擎$pdo
是核心依赖项,它不是框架或您正在开发的应用程序的依赖项。您应该在这里解决的下一个问题是如何自动化将$pdo
依赖项传递给映射器的过程。只有一个解决方案可以利用 - 工厂模式
$pdo = new PDO();
$mapperFactory = new App\Storage\MySQL\Factory($pdo);
$mapperFactory->build('UserMapper'); // will return UserMapper instance with injected $pdo dependency
现在让我们看到明显的好处:
首先,它的可读性 - 任何看到代码的人都会得到线索,Mapper被用来抽象表访问。其次,您可以轻松更换存储引擎(如果您计划将来迁移并添加多个数据库支持)
$mongo = new Mongo();
$mapperFactory = new App\Storage\Mongo\Factory($mongo);
$mapperFactory->build('UserMapper'); // will return UserMapper instance with injected $mongo dependency
注意:不同存储引擎的所有映射器都应实现一个接口(API实施)
说到网络,我们基本上会做以下事情:
因此,当您将模型实现为类时,您最终会在同一个类中编写验证和存储逻辑,因此会紧密耦合并破坏SRP。
这个常见示例之一,您可以在Yii Framework中看到
正确的模型应该是包含应用程序逻辑的类的文件夹(参见ZF2或SF2)。
开发代码时是否应该使用DI容器?好吧,让我们来看看这个经典代码示例:
class House
{
public function __construct($diContainer)
{
//Let's assume that door and window have their own dependencies
// So no, it's not a Service Locator in this case
$this->door = $diContainer->getDoor();
$this->window = $diContainer->getWindow();
$this->floor = $diContainer->getFloor();
}
}
$house = new House($di)
在这种情况下,您告诉您House
取决于DiC
,而不是明确地在门,窗和地板上。但等待我们的House
真的依赖于DiC吗?肯定没有。
当你开始测试你的课程时,你也必须提供一个准备好的DiC(完全不相关)
通常人们使用Di容器以避免一直注射。但另一方面,由于大多数DI容器都是基于配置的,因此需要花费一些RAM和一些时间进行解析。
良好的架构可以没有任何Di容器,因为它利用了工厂和SRP。
如此优秀的应用程序组件应该没有任何Service Locators和Di Containers。