我希望使用injector(或pinject)在Python中实现依赖注入,而guice本身是从Modules should be fast and side-effect free大量借用的。虽然使用Python/injector
的答案是理想的,但我对使用Java/guice
的解决方案/方法感到非常高兴。
我将快速总结一下我想要实现的目标:我有一个组件,它依赖于所有实现相同接口的其他组件的列表/序列。那些组件本身具有依赖性,这些依赖性可以在不同的实现中变化具体类型(实现)应由用户配置(或使用DI框架的任何机制)。
是的,我读过Multibinder建议不要使用XML文件进行配置,但由于我不知道如何在框架内实现这一点,我将使用一个来演示依赖结构:
<RentingAgency>
<Vehicles>
<Car>
<DieselEngine></DieselEngine>
</Car>
<Car>
<PetrolEngine></PetrolEngine>
</Car>
<Bike></Bike>
</Vehicles>
</RentingAgency>
在这个例子中,租赁代理商(依赖于其他人的清单的组件)出租各种车辆(界面)。其车队中的特定车辆(在这种情况下为两辆车和一辆自行车)应该是可配置的,但在运行期间是固定的。车辆本身可以具有依赖性,并且它们可以根据车辆的类型而不同(汽车取决于电动机,在这种情况下自行车没有依赖性)。
如何在DI框架内构建租赁代理机构,以便注入所有需要的车辆并正确解决其依赖关系?
我读过关于The docs(injector
似乎与Binder.multibind
类似的内容),它允许注入实现相同界面的对象集合。但是:
Class Car
)具有不同的电机:Interface Motor
,Class DieselEngine
,class PetrolEngine
)?Car
实例,并将所需的Motor
作为参数传递,但是因为这模式在链中进一步重复(即使用相同类型的多个Motor
并且它们也具有依赖关系)我想使用依赖注入来生成这些对象。但是要在提供程序中手动使用它们,我似乎必须直接从进样器获取实例。 {{3}}提到注入注入器是一种罕见的情况,根据我对依赖注入的理解,最大的好处是可以请求组件,所有依赖项都由框架自动解决。injector.Injector.get.__doc__
提及虽然此方法属于:class:
Injector
的公共接口 它意味着在有限的情况下使用。 例如,创建某种根对象(应用程序对象) 您的应用程序(请注意,只需要一次get
次呼叫, 在Application
类及其任何依赖项中 :func:inject
可以而且应该使用):
答案 0 :(得分:1)
依赖注入框架主要用于依赖项,因为用户在运行时配置了Vehicles
对象,它更像是应用程序数据而不是依赖项。除非你在编译时知道它,否则它可能不能只使用MultiBinding一次注入。
同样,你说得对,通过迭代和调用injector.getInstance(Bike.class)
等来构建你的组件组合不是一个好方法。首先,这不适合测试。
但是,由于Vehicles
中包含的对象具有自己的依赖关系,因此您可以在创建Vehicles
对象时利用DI框架。还要记住,虽然你不能将Provider绑定到实现,但是当你绑定一个键时,Guice会为你注入该提供者。
对于帖子中的简单示例,请考虑创建VehicleFactory
。在里面,你可能会有以下内容:
public class VehicleModule implements Module {
@Override
public void configure(Binder binder) {
binder.bind(DieselEngine.class).toProvider(DieselEngineProvider.class);
binder.bind(PetrolEngine.class).toProvider(PetrolEngineProvider.class);
binder.bind(Bike.class).toProvider(BikeProvider.class);
}
}
public class DieselEngineProvider implements Provider<DieselEngine> {
@Inject
public DieselEngineProvider() {
//if DieselEngine has any dependencies, they can be injected in the constructor
//stored in a field in the class and used in the below get() method
}
@Override
public DieselEngine get() {
return new DieselEngine();
}
}
public class VehicleFactory {
private final CarFactory carFactory;
private final Provider<Bike> bikeProvider;
@Inject
public VehicleFactory(CarFactory carFactory, Provider<Bike> bikeProvider) {
this.carFactory = carFactory;
this.bikeProvider = bikeProvider;
}
public Bike createBike() {
return bikeProvider.get();
}
public Car createDieselCar() {
return carFactory.createDieselCar();
}
public Car createPetrolCar() {
return carFactory.createPetrolCar();
}
}
public class CarFactory {
private final Provider<DieselEngine> dieselEngineProvider;
private final Provider<PetrolEngine> petrolEngineProvider;
@Inject
public CarFactory(Provider<DieselEngine> dieselEngineProvider, Provider<PetrolEngine> petrolEngineProvider) {
this.dieselEngineProvider = dieselEngineProvider;
this.petrolEngineProvider = petrolEngineProvider;
}
public Car createDieselCar() {
return new Car(dieselEngineProvider.get());
}
public Car createPetrolCar() {
return new Car(petrolEngineProvider.get());
}
}
正如你所提到的,这种情况有可能成为“工厂一路走下去”,但Guice可以帮助你。
如果Engine的生产变得更复杂并且涉及不同参数的组合,您可以使用AssistedInject之类的工具为您自动创建工厂。
如果您最终得到一组常用的依赖项和非常见的依赖项,您希望用它们来创建对象的不同“风格”,那么您就拥有了所谓的robot legs problem,然后Guice可以使用私有来解决它模块。
中的以下警告注意:注入提供程序可能会造成混淆 代码,可能是错误的结构或错误结构的设计气味 图表中的对象。通常你会想要使用工厂或工厂 懒惰或重新组织代码的生命周期和结构 只能注入一个T.
如果您遵循此建议,您似乎必须小心平衡使用提供者和使用工厂来创建Vehicle
。