c# - 带有 DI 和 IoC 的工厂方法

标签 c# dependency-injection inversion-of-control factory-pattern

我熟悉这些模式,但仍然不知道如何处理以下情况:

public class CarFactory
{
     public CarFactory(Dep1,Dep2,Dep3,Dep4,Dep5,Dep6)
     {
     }

     public ICar CreateCar(type)
     {
            switch(type)
            {
               case A:
                   return new Car1(Dep1,Dep2,Dep3);
               break;

               case B:
                   return new Car2(Dep4,Dep5,Dep6);
               break;
            }
     }
}

一般来说,问题在于需要注入(inject)的引用数量。车多了就更糟了。

我想到的第一个方法是在工厂构造函数中注入(inject) Car1 和 Car2,但它违反工厂方法,因为工厂将始终返回相同的对象。第二种方法是注入(inject) servicelocator 但它到处都是反模式。如何解决?

编辑:

备选方式1:

public class CarFactory
{
     public CarFactory(IContainer container)
     {
        _container = container;
     }

     public ICar CreateCar(type)
     {
            switch(type)
            {
               case A:
                   return _container.Resolve<ICar1>();
               break;

               case B:
                     return _container.Resolve<ICar2>();
               break;
            }
     }
}

替代方法2(由于树中的依赖太多而难以使用):

public class CarFactory
{
     public CarFactory()
     {
     }

     public ICar CreateCar(type)
     {
            switch(type)
            {
               case A:
                   return new Car1(new Dep1(),new Dep2(new Dep683(),new Dep684()),....)
               break;

               case B:
                    return new Car2(new Dep4(),new Dep5(new Dep777(),new Dep684()),....)
               break;
            }
     }
}

最佳答案

在工厂内部使用 switch case 语句是一种代码味道。有趣的是,您似乎根本没有专注于解决该问题。

这种情况下最好、最友好的 DI 解决方案是 strategy pattern .它允许您的 DI 容器将依赖项注入(inject)到它们所属的工厂实例中,而不会用这些依赖项混淆其他类或求助于服务定位器。

接口(interface)

public interface ICarFactory
{
    ICar CreateCar();
    bool AppliesTo(Type type);
}

public interface ICarStrategy
{
    ICar CreateCar(Type type);
}

工厂

public class Car1Factory : ICarFactory
{
    private readonly IDep1 dep1;
    private readonly IDep2 dep2;
    private readonly IDep3 dep3;
    
    public Car1Factory(IDep1 dep1, IDep2 dep2, IDep3 dep3)
    {
        this.dep1 = dep1 ?? throw new ArgumentNullException(nameof(dep1));
        this.dep2 = dep2 ?? throw new ArgumentNullException(nameof(dep2));
        this.dep3 = dep3 ?? throw new ArgumentNullException(nameof(dep3));
    }
    
    public ICar CreateCar()
    {
        return new Car1(this.dep1, this.dep2, this.dep3);
    }
    
    public bool AppliesTo(Type type)
    {
        return typeof(Car1).Equals(type);
    }
}

public class Car2Factory : ICarFactory
{
    private readonly IDep4 dep4;
    private readonly IDep5 dep5;
    private readonly IDep6 dep6;
    
    public Car2Factory(IDep4 dep4, IDep5 dep5, IDep6 dep6)
    {
        this.dep4 = dep4 ?? throw new ArgumentNullException(nameof(dep4));
        this.dep5 = dep5 ?? throw new ArgumentNullException(nameof(dep5));
        this.dep6 = dep6 ?? throw new ArgumentNullException(nameof(dep6));
    }
    
    public ICar CreateCar()
    {
        return new Car2(this.dep4, this.dep5, this.dep6);
    }
    
    public bool AppliesTo(Type type)
    {
        return typeof(Car2).Equals(type);
    }
}

策略

public class CarStrategy : ICarStrategy
{
    private readonly ICarFactory[] carFactories;

    public CarStrategy(ICarFactory[] carFactories)
    {
        this.carFactories = carFactories ?? throw new ArgumentNullException(nameof(carFactories));
    }
    
    public ICar CreateCar(Type type)
    {
        var carFactory = this.carFactories
            .FirstOrDefault(factory => factory.AppliesTo(type));
            
        if (carFactory == null)
        {
            throw new InvalidOperationException($"{type} not registered");
        }
        
        return carFactory.CreateCar();
    }
}

用法

// I am showing this in code, but you would normally 
// do this with your DI container in your composition 
// root, and the instance would be created by injecting 
// it somewhere.
var strategy = new CarStrategy(new ICarFactory[] {
    new Car1Factory(dep1, dep2, dep3),
    new Car2Factory(dep4, dep5, dep6)
    });

// And then once it is injected, you would simply do this.
// Note that you could use a magic string or some other 
// data type as the parameter if you prefer.
var car1 = strategy.CreateCar(typeof(Car1));
var car2 = strategy.CreateCar(typeof(Car2));

请注意,因为没有 switch case 语句,您可以在不更改设计的情况下向策略添加额外的工厂,并且每个工厂都可以有自己的依赖项,这些依赖项由 DI 容器注入(inject)。

var strategy = new CarStrategy(new ICarFactory[] {
    new Car1Factory(dep1, dep2, dep3),
    new Car2Factory(dep4, dep5, dep6),
    new Car3Factory(dep7, dep8, dep9)
    });
    
var car1 = strategy.CreateCar(typeof(Car1));
var car2 = strategy.CreateCar(typeof(Car2));
var car3 = strategy.CreateCar(typeof(Car3));

关于c# - 带有 DI 和 IoC 的工厂方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31950362/

相关文章:

c# - wince 6.0 c# 中的全屏应用程序

php - 如何避免冲突用于依赖注入(inject)的 PHP 特性

c# - StructureMap -> EnrichWith 过于丰富(其他实例)

c# - asp.net 中的应用程序本地化

c# - 如何将非表数据导入 SQL Server Reporting Services?

c# - 从 C# 类向 java 提供字符/字符串/字节流

dependency-injection - 是否有任何关于控制反转或依赖注入(inject)值(value)的硬数据?

即使在事件重新触发后 Flutter BlocListener 也只执行一次

c# - 在 Autofac 的 lambda 注册期间访问上下文信息?

c# - Autofac IComponentContext.Resolve<Type> 是服务定位器模式吗