0

我想在不传递任何“内核”容器的情况下使用基于工厂的依赖注入,这样就无法实例化一个类,而不必从“顶部”明确传递它的依赖关系。构造函数的自动工厂生成器DI​​

手动方式,这样做需要在引导这样的代码:

static void Main(string[] args) 
    { 
     // simplified example, can require classes in reality 
     ABFactory abFactory = (data1) => new AB(data1); 
     ACAFactory acaFactory = (data1) => new ACA(data1); 
     ACFactory acFactory = (x) => new AC(x, acaFactory); 
     IA a = new A(1, new AA(1, new AAA(), new AAB()), abFactory, acFactory); 
     a.Action(123); 
    } 

当工厂是指

delegate IAB ABFactory(string data1); 
delegate IAC ACFactory(int x); 
delegate IACA ACAFactory(int data1); 

有什么我可以使用,使工厂建设更容易,甚至自动?有不同的工厂类型支持(池,ThreadLocal缓存等)?

UPDATE

一些实际的代码示例:

public interface IItemSetSpawnController 
{ 
    void TransitSpawned(); 
} 

public class ItemSetSpawnController : IItemSetSpawnController 
{ 
    readonly GameMap.ItemSet _set; 
    readonly LootableFactoryDelegate _lootableFactory; 
    readonly IFiber _fiber; 

    readonly int _defaultRespawnTime; 

    public ItemSetSpawnController([NotNull] GameMap.ItemSet set, int defaultRespawnTime, [NotNull] LootableFactoryDelegate lootableFactory, IFiber fiber) 
    { 
     if (set == null) throw new ArgumentNullException(nameof(set)); 
     if (lootableFactory == null) throw new ArgumentNullException(nameof(lootableFactory)); 
     if (set.Items.Count == 0) throw new ArgumentException("Empty set", nameof(set)); 
     _set = set; 
     _lootableFactory = lootableFactory; 
     _fiber = fiber; 
     _defaultRespawnTime = defaultRespawnTime; 
    } 

    public void TransitSpawned() 
    { 
     // Fiber.Schedule for respawning 
    } 
} 

public delegate IItemSetSpawnController ItemSetSpawnControllerFactory(
    [NotNull] GameMap.ItemSet set, int defaultRespawnTime, [NotNull] LootableFactoryDelegate lootableFactory, IFiber fiber); 


protected virtual void AddMapLootables() 
{ 
    foreach (var set in ItemSets) 
    { 
     if (set.Items.Count == 0) continue; 
     var c = ItemSetSpawnControllerFactory(
      set, 
      Settings.LootRespawnTime, 
      LootableFactory, 
      ExecutionFiber); 
     c.TransitSpawned(); 
    } 

} 
+1

Autofac内置这个概念:http://docs.autofac.org/en/latest/advanced/delegate-factories.html –

回答

0

在我看来,你的组件与运行时数据初始化。这是bad practice,因为这会使你的DI配置复杂化很多,我认为你的情况就是这样。

而不是使用工厂初始化组件的构造函数依赖于服务依赖性和运行时数据的组件,将运行时数据移出构造函数并在运行时将其提供给组件。您可以通过将运行时值传递给组件的方法来完成此操作,也可以将组件注入到允许组件在运行时(初始化后)请求该值的组件中。

要选择哪个选项取决于使用运行时值是否是实现细节。如果它是抽象的重要部分,则该值应该是抽象方法签名的一部分。如果抽象无关紧要,并且只有组件本身应该了解它,那么注入允许访问此运行时值的服务是最明显的解决方案。

UPDATE

如果将运行时数据出来的构造函数,你将能够去除需要使用的工厂作为抽象的附加层,你可以将代码更改为类似这样:

public interface IItemSetSpawnController { 
    void TransitSpawned(GameMap.ItemSet set); 
} 

public class ItemSetSpawnController : IItemSetSpawnController { 
    readonly LootableFactoryDelegate _lootableFactory; 
    readonly IFiber _fiber; 
    readonly ISessings settings; 
    public ItemSetSpawnController(ISessings settings, 
     [NotNull] LootableFactoryDelegate lootableFactory, IFiber fiber) { 
     _lootableFactory = lootableFactory; 
     _fiber = fiber; 
     _settings = settings; 
    } 
    public void TransitSpawned([NotNull] GameMap.ItemSet set) { 
     if (set == null) throw new ArgumentNullException(nameof(set)); 
     // If LootRespawnTime is a config value that doesn't change after startup, you 
     // can more it back into the constructor of the controller. 
     int defaultRespawnTime = _settings.LootRespawnTime; 
     // Fiber.Schedule for respawning 
    } 
} 

class MapLooter { 
    IItemSetSpawnController controller; 
    public MapLooter(IItemSetSpawnController controller){ 
     this.controller = controller; 
    } 
    public void AddMapLootables() { 
     foreach (var set in ItemSets) { 
      if (set.Items.Count == 0) continue; 
      this.controller.TransitSpawned(set); 
     } 
    } 
} 
+0

是的,这是一种常用的方法 - 将上下文数据传递给构造函数。实际上,如果一个类没有上下文实例化,那么它应该是单例。我明白,你建议将这样的上下文初始化代码移动到一个单独的方法包括接口,但它看起来有点奇怪... – Vlad

+0

@Vlad:如果您更新您的问题,并使您的代码更具体(与实际名称您正在尝试创建的抽象和组件),我可以举一个如何改进设计的具体示例。 – Steven

+0

好的,我更新了它 – Vlad