我已经开始重构一个小应用程序来使用一个小的DI容器而不是 $注册表中:getstuff();在我的课程中调用我将它们注入容器中。
这提出了两个问题,
Q1 - >我扩展Pimple DI class并创建一个容器,其中包含特定于需要DI的每个对象的依赖项。然后我提供对象the whole shebang,并在构造函数中将其解析为将DI的对象分配给我正在构建的对象的类属性。
我应该在新对象()调用中分离对象吗?我觉得这样比较容易,但现在看到我是一个单人团队,我只想确认我有适当的方法。
Q2 - >我发现如果我在一些主要类上执行此操作,我遍布的$ registry对象将是无效的,这是否是使用DI的正常结果,没有更多的注册表?我可能在容器中注入了一个或两个单例,但它看起来就像我将需要的一样,甚至那些可以很容易地被淘汰,因为DI有一个share()属性,它返回对象的同一个实例,有效地消除了需要对于单身人士。这是摆脱应用程序需要注册表/单身人士的方式,因为如果它是这样的那么容易。
答案 0 :(得分:19)
Q2:
如果你在$registry
对象周围传遍......那么你的注册表并不是所谓的注册表(正如福勒所描述的那样)。
注册表或多或少是一个带有get / set方法的全局对象(“众所周知”)。 在PHP中,用于实现 Registry 的两个常见原型将是
作为单身人士
class RegistryAsSingleton
{
public static function getInstance (){
//the singleton part
}
public function getStuff ()
{
//some stuff accessed thanks to the registry
}
}
使用静态方法
class RegistryAsStatic
{
public static function getStuff()
{
}
}
将注册表传递到整个地方使得它只是一个对象:一个容器,其目的不是提供对其他对象的引用。
您的DI容器(使用您在OP中建议的Pimple)是一种注册表本身:它是众所周知的,使您可以从任何地方获取组件。
所以是的,我们可以说你的DI容器将通过执行相同的功能来删除注册表的要求和必要性。
但是(总有一个但是)
注册表总是有罪,直到 被证明是无辜的 (马丁福勒)
如果您使用 DI容器替换注册表,这可能是错误的。
例如:
//probably a Wrong usage of Registry
class NeedsRegistry
{
public function asAParameter(Registry $pRegistry)
{
//Wrong dependency on registry where dependency is on Connection
$ct = $pRegistry->getConnection();
}
public function asDirectAccess ()
{
//same mistake, more obvious as we can't use another component
$ct = Registry::getInstance()->getConnection();
}
}
//probably a wrong replacement for Registry using DI Container
class NeedsContainer
{
public function asAParameter(Container $pRegistry)
{
//We are dependent to the container with no needs,
//this code should be dependent on Connection
$ct = $pContainer->getConnection();
}
public function asDirectAccess ()
{
//should not be dependent on container
$ct = Container::getInstance()->getConnection();
}
}
为什么这么糟糕?因为你的代码依赖程度不低于以前,所以它依赖于一个组件(注册表或容器),它没有提供明确的目标(我们可能会想到这里的接口)
注册表模式在某些情况下很有用,因为它是一种简单且相当便宜的方法来定义组件或数据(例如全局配置)。
通过删除依赖关系来重构上述示例而不依赖于DI的方法是:
class WasNeedingARegistry
{
public function asAParameter (Connection $pConnection)
{
$pConnection->doStuff();//The real dependency here, we don't care for
//a global registry
}
}
//the client code would be like
$wasNeedingARegistry = new WasNeedingARegistry();
$wasNeedingARegistry->setConnection($connection);
当然,如果客户端代码不知道连接,这可能是不可能的,这可能是您可能首先使用注册表的原因。
现在DI发挥作用
使用DI使我们的生活变得更好,因为它将处理依赖性并使我们能够在即用状态下访问依赖项。
在代码的某处,您将配置组件:
$container['connection'] = function ($container) {
return new Connection('configuration');
};
$container['neededARegistry'] = function ($container) {
$neededARegistry = new NeededARegistry();
$neededARegistry->setConnection($container['connection']);
return $neededARegistry;
};
现在,您拥有了重构代码所需的一切:
// probably a better design pattern for using a Registry
class NeededARegistry
{
public function setConnection(Connection $pConnection)
{
$this->connection = $pConnection;
return $this;
}
public function previouslyAsDirectAccess ()
{
$this->connection->doStuff();
}
}
//and the client code just needs to know about the DI container
$container['neededARegistry']->previouslyAsDirectAccess();
“客户端”代码应尽可能隔离。客户端应该负责并注入自己的依赖项(通过set-
方法)。客户端不应负责处理其依赖项的依赖项。
class WrongClientCode
{
private $connection;
public function setConnection(Connection $pConnection)
{
$this->connection = $pConnection;
}
public function callService ()
{
//for the demo we use a factory here
ServiceFactory::create('SomeId')
->setConnection($this->connection)
->call();
//here, connection was propagated on the solely
// purpose of being passed to the Service
}
}
class GoodClientCode
{
private $service;
public function setService(Service $pService)
{
//the only dependency is on Service, no more connection
$this->service = $pService;
}
public function callService ()
{
$this->service->setConnection($this->connection)
->call();
}
}
DI容器将使用已正确配置其连接
的服务配置GoodClientCode至于Singleton方面,是的,它可以让你摆脱它们。 希望这有帮助