Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
172 views
in Technique[技术] by (71.8m points)

c# - Why would you use Windsor AsFactory?

Why would you use Castle Windsor factory auto implementation feature: AsFactory() rather then asking for needed interface?

Example:

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

Why wouldn't you simply write:

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

Effect is the same. Windsor will resolve IEmailSender as default registered implementation anyway so what is the point?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

1. To inject specific constructor arguments

Sometimes you'll write a class that requires a specific value when you need to resolve it. For instance:

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

You can't resolve an instance of this class without a number, and maybe you'd also like to specify the stream (console, file, printer, whatever). So, you define a factory:

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

Now the following code will work:

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. To introduce a level of abstraction

Using a factory can insulate you from changes to how you'll need to create an object. For instance, if you'll need to create instances of objects and always use the same constructor parameter each time, you can create a concrete factory and then just use that everywhere instead of the one generated via AsFactory().

In other words, we could modify the behavior some of the code by burying the stream parameter in the factory so that a specific default stream is always used (for instance if an IStream cannot just be resolved from the container). Doing it this way means we wouldn't need to change the RandomNumberGenerator at all:

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

And no change in RandomNumberGenerator, but the behavior is changed:

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
}

Again, this is useful in the sense that if you were already using a factory interface, such as one implemented by using AsFactory(), you could easily swap it out for a new implementation. Doing this if you were already using a container instead is more difficult; it's harder to find the places you need to change and it's harder to swap out usage of the container to use a new type (i.e. a factory).

Note: you would have to create an INumberWriterFactoryFactory to inject the IStream into the concrete factory.

3. To keep usage of the IOC container in the composition root

There's a lot of folk who subscribe to the idea that there is only one composition root, and it is the only time a reference to the IOC container is permitted. Doing so can help you to avoid several anti-patterns, such as the service locator pattern.

Please note that these examples are dumb, but I hope they get the points across.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...