DI 的主要目的是注入依赖项,而不是注入负责解析/创建对象的对象。在您的示例中,假设我们想使用houseFactory
类来获取house
类并使用它(例如 goHome 方法)。
$house = $houseFactory->createObject();
$house->goHome();
该实现不仅使您的调用者依赖于house
类,而且还依赖于houseFactory
类,这是一种无用的依赖。
正如 TrueWill 所说,应用程序唯一负责创建对象的地方是composition root
. 这composition root
是每个应用程序的起点。不幸的是,我认为纯 php 类中没有任何组合根,但是在 CI 或 Zend 等 php 框架中应该有。
为什么我们要在 中创建对象composition root
?
- 使对象创建的范围变得很小。组合根通常只包含启动应用程序的小逻辑。也许其他一些逻辑来处理一般异常,但不是其他的。因此创建对象是安全的,因为那里不存在特定的业务逻辑。
- 组合根不是在其他类中创建的。它被称为应用程序启动,因此在类中创建对象是安全的。
要了解有关组合根的更多信息,您可以阅读 Mark Seeman 的书或他的博客。
那么我们如何访问在组合根目录中创建的对象呢?
有些应用程序对 DI 有很好的支持,例如 ASP.Net MVC,但有些应用程序不像 ASP.Net Webform。在具有良好支持的应用程序中,您可以直接从composition root
. 但是,如果没有,也许您可以通过静态类访问组合根 DI 容器,但只能在页面的构造函数中。因此,它最大限度地减少了对组合根的访问。
编辑1:
这是用 php 编写的没有 DI Container 的 house 类的使用示例(可能不工作,没有在 php 中使用 DI)。
class houseConsumer{
protected $_house;
public function __construct(\DI $house)
{
$this->_house($house)
}
public function doSomething(){
// do something
$this->_house->goHome();
}
}
class compositionRoot{
public function main(){
$diContainer = // define the DI Container
$houseConsumer = new houseConsumer($diContainer->create('house'));
$houseConsumer->doSomething();
}
}
这是 C# 控制台中的示例:
public class HouseConsumer{
//constructor
public HouseConsumer(IHouse house){
this.house = house;
}
IHouse house;
public void DoSomething(){
house.GoHome();
}
}
//this is the composition root
public class Program{
public static void Main(string[] args){
List<object> diContainer = new List<object>();
//populate diComponent
HouseConsumer consumer = new HouseConsumer((IHouse)diContainer[0]);
consumer.DoSomething();
}
}
编辑2:
DI 容器实际上是否在“主要”方法之外使用?如何在“主”方法之外创建新对象?可以说我有几个对象深。我如何在那里建造房子?
我上面的示例可以在 Asp.Net MVC 中使用,其中页面也使用 IOC 容器自动解析。不幸的是,如果你需要像 Asp.Net webforms 这样的页面中的 DI 容器,你应该使用静态,但只在构造函数中分配对象。示例(在 C# 中,我不知道如何在 php 中使用静态)。
public class Page1 : Page{
public Page1(){
this.house = CompositionRoot.DiContainer.Create("house");
}
private IHouse house;
}
这种实现仍然是安全的,因为 DIContainer 仅在页面的构造函数级别使用。但请确保不要在 Service 类中执行此操作。例子:
public class Renter : IRenter{
public Renter(){
this.house = CompositionRoot.DiContainer.Create("house");
}
private IHouse house;
}
是一个糟糕的设计。Renter 类依赖于 DIContainer。为了解决这个问题,注入IHouse
类。
public class Renter : IRenter{
public Renter(IHouse house){
this.house = house;
}
private IHouse house;
}
那么Renter类的调用者应该通过house类。该示例是使用穷人的将 IRenter 类分配给 DIContainer。
Dictionary<Type, object> DiContainer = new Dictionary<Type, object>();
IHouse house = new House();
IRenter renter = new Renter(house);
DiContainer.Add(typeof(IHouse), house);
DiContainer.Add(typeof(IRenter), renter);
然后在 Page 中使用它:
public class Page1 : Page{
public Page1(){
this.renter = (IRenter)CompositionRoot.DiContainer[typeof(IRenter)];
}
private IRenter renter;
}