你为什么要使用Windsor AsFactory?

时间:2014-07-31 15:14:15

标签: c# castle-windsor

为什么要使用Castle Windsor工厂自动实现功能:AsFactory()而不是要求所需的接口?

示例:

container.Register(Component.For<IEmailSender>().ImplementedBy<SmtpEmailSender>());
container.Register(Component.For<IEmailSenderFactory>().AsFactory().LifeStyle.Transient);

...

using (var factory = context.GetServiceFactory<IEmailSenderFactory>())
{
    var emailSender = factory.CreateEmailSender();
    emailSender.Send(message);
}

为什么你不写简单:

var emailSender = context.GetServiceFactory<IEmailSender>();
emailSender.Send(message);

效果是一样的。无论如何,Windsor会将IEmailSender解析为默认注册实现,那又有什么意义呢?

1 个答案:

答案 0 :(得分:39)

1。注入特定的构造函数参数

有时,当您需要解决它时,您将编写一个需要特定值的类。例如:

public class NumberWriter : INumberWriter
{
    readonly int number;
    readonly IStream stream;

    public NumberWriter(int number, IStream stream)
    {
        this.number = number;
        this.stream = stream;
    }

    public Write()
    {
        stream.Write(number);
    }
}

您无法在没有number的情况下解析此类的实例,也许您还想指定stream(控制台,文件,打印机等)。所以,你定义一个工厂:

public interface INumberWriterFactory
{
    INumberWriter Create(int number);
    INumberWriter Create(int number, IStream stream);
} 

现在,以下代码将起作用:

public class RandomNumberGenerator
{
    readonly INumberWriterFactory numberWriterFactory;

    public RandomNumberGenerator(INumberWriterFactory numberWriterFactory)
    {
        this.numberWriterFactory = numberWriterFactory;
    }

    public void Generate()
    {
         Random random = new Random();
         for (int i = 0; i < 10; i++)
         {
             // Writes to first IStream that Castle can resolve
             var numberWriter = numberWriterFactory.Create(random.Next());
             numberWriter.Write();
         }
    }

    public void Generate(IStream stream)
    {
         Random random = new Random();
         for (int i = 0; i < 10; i++)
         {
             // Writes to the given IStream
             var numberWriter = numberWriterFactory.Create(random.Next(), stream);
             numberWriter.Write();
         }
    }
}

2。引入抽象级别

使用工厂可以使您无需更改创建对象的方式。例如,如果您需要创建对象实例并且每次始终使用相同的构造函数参数,则可以创建具体工厂,然后只使用该工厂而不是通过AsFactory()生成的工厂。 / p>

换句话说,我们可以通过在工厂中隐藏stream参数来修改某些代码的行为,以便始终使用特定的默认流(例如,如果IStream不能只是从容器中解决)。这样做意味着我们根本不需要更改RandomNumberGenerator

public class NumberWriterFactory : INumberWriterFactory
{
    readonly IStream stream;
    readonly IContainer container;

    public NumberWriterFactory(IStream stream, IContainer container)
    {
        this.stream = stream;
        this.container = container;
    }

    public INumberWriter Create(int number)
    {
        return container.Resolve<INumberWriter>(number, this.stream);
    }

    public INumberWriter Create(int number, IStream stream)
    {
        return container.Resolve<INumberWriter>(number, stream);
    }
}

RandomNumberGenerator没有变化,但行为发生了变化:

public class RandomNumberGenerator
{
    readonly INumberWriterFactory numberWriterFactory;

    public RandomNumberGenerator(INumberWriterFactory numberWriterFactory)
    {
        this.numberWriterFactory = numberWriterFactory;
    }

    public void Generate()
    {
         Random random = new Random();
         for (int i = 0; i < 10; i++)
         {
             // Writes to the IStream instance that the factory contains
             var numberWriter = numberWriterFactory.Create(random.Next());
             numberWriter.Write();
         }
    }  

    // the rest as before
}

同样,这是有用的,如果您已经在使用工厂界面,例如使用AsFactory()实现的界面,您可以轻松地将其换出以实现新的实现。如果您已经使用容器,那么这样做会更加困难;找到你需要改变的地方更加困难,更换容器的使用更难以使用新的类型(即工厂)。

注意:您必须创建INumberWriterFactoryFactory以将IStream注入具体工厂。

3。保持在组合根

中使用IOC容器

有很多人认为只有一个composition root,而时间是允许对IOC容器的引用。这样做可以帮助您避免多种反模式,例如service locator模式。

请注意,这些例子很愚蠢,但我希望他们能够得到这些观点。