使用UsingFactoryMethod

时间:2016-04-25 21:20:16

标签: c# generics dependency-injection inversion-of-control castle-windsor

我们目前的代码看起来如下所示,工厂被注入很多类,然后调用工厂获取他们想要的实例。

public class Service
{
     public Service(IFactory factory)
     {
          _car = factory.GetCar<Entity>();
     }
}

public class Car : ICar
{
}

public interface IFactory
{
    ICar<TEntity> GetCar<TEntity>();

    IBoat<TEntity> GetBoat<TEntity>();
}

public class Factory : IFactory
{
    ConnectionDetails _connectionDetails;    

    public Factory(ConnectionDetails connectionDetails)
    {
        _connectionDetails = connectionDetails;
    }

    TEntity GetCar<TEntity>()
    {
        var car = new Car<TEntity>(_connectionDetails);
        car.Initialize();
        return car;
    }
}

我希望能够创建一个解决方案,允许直接在Car<TEntity>上请求依赖,而无需先通过工厂。

以下是安装单个TEntity的示例,但我如何将其设置为通用?

我尝试过使用开放式泛型,但我无法看到如何从.UsingFactoryMethod()中获取正确的返回类型。

我知道我可以从RequestedType中获取CreationContext,但我不知道如何使用它来解决此问题。

public class Installer : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
                    Component.For<ICar<TEntity>>().UsingFactoryMethod(
                        kernel =>
                        {
                            var factory = kernel.Resolve<IFactory>();
                            return factory.GetCar<TEntity>();
                        }));
    }
}

2 个答案:

答案 0 :(得分:1)

我个人发现混合了依赖注入的工厂可能有点反模式,因为它隐藏了除了对象图根之外的其他地方的实现/创建细节。此外,当混合这两者时,不清楚谁最终有责任创建和维护对象及其生命周期。

我建议你转而允许容器根据公共基本接口/类完全处理创建细节。

void Main()
{
    var container = new WindsorContainer();
    container.Install(new Installer());

    var car = container.Resolve<Car>();
    car.Dump();
}

public class Service
{
    private ICar<CarEntity> _car;

    public Service(ICar<CarEntity> car)
    {
        _car = car;
    }
}

public class TEntity { }
public class CarEntity : TEntity { }
public class BoatEntity : TEntity { }

public interface ICreatable { }
public interface ICar<TEntity> : ICreatable { }

public class Car : ICar<TEntity> 
{
    private ConnectionDetails _connectionDetails;

    public Car(ConnectionDetails connectionDetails)
    {
        _connectionDetails = connectionDetails; 
        Initialize();
    }

    public void Initialize() {}
}

public class ConnectionDetails { }

public class Installer : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component.For<ConnectionDetails>()
                     .ImplementedBy<ConnectionDetails>());

        container.Register(
            Classes.FromAssemblyInThisApplication()
                .BasedOn(typeof(ICreatable))
                .WithServiceAllInterfaces()
                .WithServiceSelf()
                .LifestyleTransient());
    }
}

答案 1 :(得分:1)

你可以使用Castle的Typed Factory Facility来实现这一目标。

首先,您需要创建Castle将实现的接口:

public interface IFactory
{
    ICar<TEntity> CreateCar<TEntity>(ConnectionDetails connectionDetails);
    IBoat<TEntity> CreateBoat<TEntity>(ConnectionDetails connectionDetails);
    void Release(object component); // left type as object so it can release either a boat or a car
}

使用此实现,只要您的对象在封闭泛型类型的容器中注册为其服务名称,castle就会自动找到正确的对象。

然后您只需添加设施并注册工厂界面:

kernel.AddFacility<TypedFactoryFacility>();
kernel.Register(Component.For<IFactory>().AsFactory());

我在顶部链接的文章也涵盖了界面中方法的命名语义,因为某些词具有特殊含义。