Scenario: You need to select an implementation of address validation when a sales order is submitted, and the validator is determined by the country to which the order is shipping. The factory needs to inspect some value passed as an argument to select the correct implementation.
First, write an interface for the factory:
public interface IAddressValidatorFactory
{
IAddressValidator GetAddressValidator(Address address);
void Release(IAddressValidator created);
}
The interface indicates that the implementation of IAddressValidator
will be determined by the Address
passed as an argument to GetAddressValidator
.
When we register multiple implementations of the same interface with Windsor we typically name them. So the factory will need to return the name of an implementation. In this case, let's say we have several implementations of IAddressValidator
registered with our container:
container.Register(
Component.For<IAddressValidator,UnitedStatesAddressValidator>()
.Named("AddressValidatorFor_USA"),
Component.For<IAddressValidator, FinlandAddressValidator>()
.Named("AddressValidatorFor_FIN"),
Component.For<IAddressValidator, MalawiAddressValidator>()
.Named("AddressValidatorFor_MWI"),
Component.For<IAddressValidator, CommonCountryAddressValidator>()
.Named("FallbackCountryAddressValidator")
.IsDefault()
);
The job of our factory will to take an Address
and return the name of the implementation that Windsor will resolve. That requires a component selector.
public class AddressValidatorSelector : DefaultTypedFactoryComponentSelector
{
public AddressValidatorSelector()
: base(fallbackToResolveByTypeIfNameNotFound: true) { }
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return "AddressValidatorFor_" + ((Address)arguments[0]).CountryCode;
}
}
This class tells Windsor to look for an implementation of IAddressValidator
named according to the country code of the order. For Finland that's "AddressValidatorFor_Fin".
And if there is no validator for that specific country we can fall back to a default validator. We could also use the same validator for multiple countries by registering the same implementation more than once with different names.
We wrote the code for the component selector, but we don't write the code for the factory itself. Windsor provides the factory, and we tell it to use our AddressValidatorSelector
.
// Add the factory facility once.
container.AddFacility<TypedFactoryFacility>();
container.Register(
Component.For<IAddressValidatorFactory>()
.AsFactory(new AddressValidatorSelector()));
When a class has a dependency on IAddressValidatorFactory
Windsor will inject its own implementation using the component selector we supplied.