Factory that returns multiple implementations of the same interface

A few developers and I are attempting to refactor a class that has grown too large. Currently this class is around 3K lines long. The goal of the refactor is to make the logic more maintainable.

The class holds our validation logic, which performs various checks to make sure things are good before moving to the next step of the process.

Currently each validation is a function of the validation class. They all have the same method signature.

One idea we had is to create a class for each validation, with a factory to give us a list of all validations that are needing to run.

A simple implementation of this is below:

Here’s the validation interface:

public interface IValidationType
{
    bool Validate();
}

With an implementation looking like this:

public class ValidationType1 : IValidationType
{
    private readonly IDependency1 _dependency1;

    public ValidationType1(IDependency1 dependency1)
    {
        _dependency1 = dependency1;
    }

    public bool Validate()
    {
        return _dependency1.SomeFunction();
    }
}

With a factory class that accepts IValidationType’s as dependencies:

public class ValidationFactory : IValidationFactory
{
    private readonly IList<IValidationType> _validations;

    public ValidationFactory(
        IValidationType validation1,
        IValidationType validation2)
    {
        _validations = new List<IValidationType>
        {
            validation1,
            validation2
        };
    }

    public IEnumerable<IValidationType> RetrieveValidations()
    {
        return _validations;
    }
}

Currently our plan is to abuse our IOC container to handle the dirty work with named registers:

public class ValidationAutofacModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<ValidationType1>().Named<IValidationType>("Validation1");
        builder.RegisterType<ValidationType2>().Named<IValidationType>("Validation2");

        builder.Register<IValidationFactory>(
            x => new ValidationFactory(
                x.ResolveNamed<IValidationType>("Validation1"),
                x.ResolveNamed<IValidationType>("Validation2")));
    }
}

One of our concerns with this implementation is that for each new validation created the developer would need to register it in the factory, and also in the IOC container. It also smells having to abuse the IOC container to get this to work.

Is this an anti-pattern? Is there a better way?

10

Asking for a collection of all implementations of one interface can be an effective pattern.

When your interface has a natural composite implementation, this is a good way to allow extension points. I think of this as a “plugin” approach: anyone can write a new implementation of your interface and its behavior gets naturally mixed in with all the other implementations.

A “natural composite” is a valid implementation of your interface that delegates to its component implementations and combines their results in a way that is natural to its purpose. Your case is straightforward: to implement Validate in the composite, just call Validate on all components and combine the results with and.

Named dependencies and factories are not the right way to handle this.

Yes, you are abusing your IOC container. The principle of DI is to ask for what you need. You need an IEnumerable<IValidationType> (an IList is inappropriate here because you should not be modifying it and don’t need random access). But instead of just asking for an IEnumerable<IValidationType>, you’re asking for two different implementations. As you identified, this violates open/closed because your factory will change every time you come up with a new implementation.

I also don’t understand the point of your factory. It seems to have 0 responsibilities. Instead of the factory, write the composite.

public class CompositeValidationType : IValidationType
{
    public CompositeValidationType(IEnumerable<IValidationType> components) { ... }
    public bool Validate() { return components.All(o => o.Validate()); }
}

Then get your IOC container to register this composite with the right component implementations as the canonical IValidationType. Reflection can be your friend here.

I am vehemently against ever using named dependencies; it breaks the sensible rule that renaming a method parameter should always be a safe refactor.

Use a single factory

I would use a single factory for all validators. The single factory can in turn expose methods for retrieving validators that are appropriate, which can be called by the class that receives the injection, instead of taking the dependency directly. E.g. instead of this

public MyClass(IInjected injected)  //Constructor
{
    _dependency = injected;
}

…follow this pattern:

public MyClass(IInjected injected) //Constructor
{
    _dependency = injected.RetrieveDependency(arguments);
}

How to retrieve the right type

The One Factory can retrieve all of the validators from the IoC container by resolving an IEnumerable<IValidationType>. Once it has the list, it can fulfill requests to return validators by selecting from the master list, using logic that you have complete control over.

The factory can expose different methods for finding the right validators. Here are three common patterns:

  • The way you do it now, e.g.
    RetrieveValidations("Validation1")

  • Passing a type argument, e.g. RetrieveValidations<ValidationType1>()

  • Passing some item that the type system knows is associated with the item. E.g. a validator for text might use RetrieveValidationsFor<string>().

I recommend the last method since it will catch many errors at compile time that would not be caught with the other two techniques.

Extending the type system to mark which validators are correct

To avoid passing around magic strings to retrieve named registrations, you can use the type system to indicate which validators go with which types. In this example I have three validators that are good for strings, ints, and DateTimes; you can follow this pattern to support objects that your business is interested in.

public interface IValidationType
{
    bool Validate();
}

public interface IValidatorFor<T> : IValidationType
{
}

public class ValidationType1 : IValidatorFor<string>, IValidationType
{
    public bool Validate() { return true; }
}

public class ValidationType2 :IValidatorFor<int>,  IValidationType
{
    public bool Validate() { return true; }
}

public class ValidationType3 :IValidatorFor<DateTime>,  IValidationType
{
    public bool Validate() { return true; }
}

Examples

Here is an example factory, where I show the three different ways of passing the type information to get the right validator:

public class ValidationFactory : IValidationFactory
{
    private readonly IList<IValidationType> _validations;

    public ValidationFactory(ILifetimeScope container)
    {
        _validations = container.Resolve<IEnumerable<IValidationType>>().ToList();
    }

    public IEnumerable<T> RetrieveValidations<T>() where T : IValidationType
    {
        return _validations.OfType<T>();
    }

    public IEnumerable<IValidationType> RetrieveValidations(string validationName) 
    {
        return _validations.Where( v => v.GetType().FullName.Contains(validationName) );  //or some other logic depending on your problem domain
    }

    public IEnumerable<IValidatorFor<T>> RetrieveValidationsFor<T>()
    {
        return _validations.OfType<IValidatorFor<T>>();
    }
}

And some test classes to show you how the injection works:

public class Example1
{
    protected readonly IEnumerable<IValidationType> _validations;

    public Example1(IValidationFactory factory)
    {
        _validations = factory.RetrieveValidations<ValidationType1>().ToList();
    }

    public void Run()
    {
        Console.WriteLine("Example validators to be used:");
        foreach (var f in _validations)
        {
            Console.WriteLine("{0}", f.GetType().FullName);
        }
    }
}

public class Example2
{
    protected readonly IEnumerable<IValidationType> _validations;

    public Example2(IValidationFactory factory)
    {
        _validations = factory.RetrieveValidations("2").ToList();
    }

    public void Run()
    {
        Console.WriteLine("Example2 validators to be used:");
        foreach (var f in _validations)
        {
            Console.WriteLine("{0}", f.GetType().FullName);
        }
    }
}

public class Example3
{
    protected readonly IEnumerable<IValidationType> _validations;

    public Example3(IValidationFactory factory)
    {
        _validations = factory.RetrieveValidationsFor<DateTime>().ToList();
    }

    public void Run()
    {
        Console.WriteLine("Example3 validators to be used:");
        foreach (var f in _validations)
        {
            Console.WriteLine("{0}", f.GetType().FullName);
        }
    }
}

Here is a small test program:

public class Application
{
    protected readonly Example1 _example1;
    protected readonly Example2 _example2;
    protected readonly Example3 _example3;

    public Application(Example1 example1, Example2 example2, Example3 example3)
    {
        _example1 = example1;
        _example2 = example2;
        _example3 = example3;
    }

    public void Run()
    {
        _example1.Run();
        _example2.Run();
        _example3.Run();
    }
}

public class Program
{

    public static IContainer CompositionRoot()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<ValidationType1>().As<IValidationType>();
        builder.RegisterType<ValidationType2>().As<IValidationType>();
        builder.RegisterType<ValidationType3>().As<IValidationType>();
        builder.RegisterType<ValidationFactory>().As<IValidationFactory>();
        builder.RegisterType<Example1>();
        builder.RegisterType<Example2>();
        builder.RegisterType<Example3>();
        builder.RegisterType<Application>();
        return builder.Build();
    }

    public static void Main()
    {
        CompositionRoot().Resolve<Application>().Run();
    }
}

And the output:

Example validators to be used:
Example.ValidationType1
Example2 validators to be used:
Example.ValidationType2
Example3 validators to be used:
Example.ValidationType3

See the full working example on DotNetFiddle

Note: Some people might say it is “bad” for a factory to access the container. This is normally true (it leads to something which looks an awful lot like the service locator anti-pattern) but for a factory it is okay. The very purpose of the factory is to assemble the dependencies to construct objects, and as such they serve as an extension of the composition root.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật