我正在努力解决依赖注入问题,并且我理解它,在大多数情况下。
但是,如果由于某种原因,我的一个类依赖于几个类,而不是将所有这些传递给构造函数中的这个类,是否有更好、更明智的方法?
我听说过 DI Containers,这就是我要解决这个问题的方法吗?我应该从哪里开始使用这个解决方案?我是否将依赖项传递给我的 DIC,然后将其传递给需要这些依赖项的类?
任何可以为我指明正确方向的帮助都会很棒。
我正在努力解决依赖注入问题,并且我理解它,在大多数情况下。
但是,如果由于某种原因,我的一个类依赖于几个类,而不是将所有这些传递给构造函数中的这个类,是否有更好、更明智的方法?
我听说过 DI Containers,这就是我要解决这个问题的方法吗?我应该从哪里开始使用这个解决方案?我是否将依赖项传递给我的 DIC,然后将其传递给需要这些依赖项的类?
任何可以为我指明正确方向的帮助都会很棒。
人们真的应该停止混淆他们。依赖注入是来自依赖倒置原则的想法。
DIC 是“灵丹妙药”,它承诺让您使用依赖注入,但在 PHP 中通常是通过打破面向对象编程的所有其他原则来实现的。最糟糕的实现也倾向于通过静态Registry
或Singleton
.
无论如何,如果你的类依赖于太多其他类,那么一般来说,这意味着类本身存在设计缺陷。你基本上有一个类有太多改变的理由,因此,打破了单一责任原则。
在这种情况下,依赖注入容器只会隐藏底层设计问题。
如果您想了解有关依赖注入的更多信息,我建议您观看 youtube 上的“清洁代码讲座”:
如果您有多个依赖项要处理,那么可以使用 DI 容器作为解决方案。
DI 容器可以是由您需要的各种依赖对象构成的对象或数组,这些对象被传递给构造函数并解包。
假设您需要一个配置对象、一个数据库连接和一个传递给每个类的客户端信息对象。您可以创建一个包含它们的数组:
// Assume each is created or accessed as a singleton, however needed...
// This may be created globally at the top of your script, and passed into each newly
// instantiated class
$di_container = array(
'config' = new Config(),
'db' = new DB($user, $pass, $db, $whatever),
'client' = new ClientInfo($clientid)
);
并且您的类构造函数接受 DI 容器作为参数:
class SomeClass {
private $config;
private $db;
private $client;
public function __construct(&$di_container) {
$this->config = $di_container['config'];
$this->db = $di_container['db'];
$this->client = $di_container['client'];
}
}
除了我上面所做的数组(这很简单),您还可以将 DI 容器创建为一个类本身,并使用单独注入其中的组件类来实例化它。使用对象而不是数组的一个好处是,默认情况下,它将通过引用传递给使用它的类,而数组则通过值传递(尽管数组中的对象仍然是引用)。
在某些方面,对象比数组更灵活,尽管最初的代码更复杂。
容器对象也可以在其构造函数中创建/实例化包含的类(而不是在外部创建它们并将它们传入)。这可以为使用它的每个脚本节省一些编码,因为您只需要实例化一个对象(它本身会实例化其他几个对象)。
Class DIContainer {
public $config;
public $db;
public $client;
// The DI container can build its own member objects
public function __construct($params....) {
$this->config = new Config();
// These vars might be passed in the constructor, or could be constants, or something else
$this->db = new DB($user, $pass, $db, $whatever);
// Same here - the var may come from the constructor, $_SESSION, or somewhere else
$this->client = new ClientInfo($clientid);
}
}
我写了一篇关于这个问题的文章。想法是使用抽象工厂和依赖注入的组合来实现(可能嵌套的)依赖的透明依赖解析。我将在此处复制/粘贴主要代码片段:
namespace Gica\Interfaces\Dependency;
interface AbstractFactory
{
public function createObject($objectClass, $constructorArguments = []);
}
抽象工厂实现是:
namespace Gica\Dependency;
class AbstractFactory implements \Gica\Interfaces\Dependency\AbstractFactory, \Gica\Interfaces\Dependency\WithDependencyInjector
{
use WithDependencyInjector;
/**
* @param string $objectClass
* @param array $constructorArguments
* @return object instanceof $class
*/
public function createObject($objectClass, $constructorArguments = [])
{
$instance = new $objectClass(...$constructorArguments);
$this->getDependencyInjector()->resolveDependencies($instance);
return $instance;
}
}
依赖注入器是这样的: namespace Gica\Dependency;
class DependencyInjector implements \Gica\Interfaces\Dependency\DependencyInjector
{
use \Gica\Traits\WithDependencyContainer;
public function resolveDependencies($instance)
{
$sm = $this->getDependencyInjectionContainer();
if ($instance instanceof \Gica\Interfaces\WithAuthenticator) {
$instance->setAuthenticator($sm->get(\Gica\Interfaces\Authentication\Authenticator::class));
}
if ($instance instanceof \Gica\Interfaces\WithPdo) {
$instance->setPdo($sm->get(\Gica\SqlQuery\Connection::class));
}
if ($instance instanceof \Gica\Interfaces\Dependency\WithAbstractFactory) {
$instance->setAbstractFactory($sm->get(\Gica\Interfaces\Dependency\AbstractFactory::class));
}
//... all the dependency declaring interfaces go below
}
}
依赖容器是标准容器。客户端代码可能如下所示:
$abstractFactory = $container->get(\Gica\Interfaces\Dependency\AbstractFactory::class);
$someHelper = $abstractFactory->createObject(\Web\Helper\SomeHelper::class);
echo $someHelper->helpAction();
注意依赖是隐藏的,我们可以专注于主要业务。我的客户端代码不关心也不知道 $someHelper 需要 anAuthenticator
或 helpAction 需要 anSomeObject
来完成它的工作;
在后台发生了很多事情,检测、解决和注入了很多依赖项。请注意,我不使用new
运算符来创建$someObject
. 实际创建对象的责任被传递给AbstractFactory
PS Gica是我的昵称 :)
我建议您使用单音或多音。在这些情况下,您将始终能够通过静态类的方法获取对象。
另一种方法(找不到正确的模式名称,但可能是Registry
)是使用一个全局静态对象来存储多个对象的实例。例如(简化代码,没有任何检查):
class Registry {
private static $instances = array();
public static function add($k, $v) {
$this->instances[$k] = $v;
}
public static function get($k) {
return $this->instances[$k];
}
}
class MyClass {
public function __construct() {
Registry::add('myclass', $this);
}
}