繁体   English   中英

构造函数DI的自动工厂生成器

[英]Automatic factory generators for constructor 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缓存等)?

更新

一些真实的代码示例:

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();
    }

}

在我看来,您的组件已使用运行时数据进行了初始化。 这是不好的做法 ,因为这会使您的DI配置变得非常复杂,我认为您的情况就是如此。

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

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

更新

如果将运行时数据移出构造函数,则无需使用工厂作为抽象层,您可以将代码更改为以下形式:

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);
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM