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

Смежные вопросы