Should a factory have a constructor with parameters?
Asked Answered
B

3

6

Let's say I want to build a list of strings (Which is not the real scenario case, but sounds simpler to explain).

I'd have an interface for my list of strings factory that would look like this

public interface IStringsListFactory{
   List<string> Create();
}

But lets say one of my concrete factory would require to get this list of string from a file/database etc..

public class StringsListFromFile : IStringsListFactory{

   private StreamReader _streamReader;
   public StringsListFromFile(StreamReader sr) //StreamReader is just an example.
   {
       _streamReader = sr;
   }

   public List<string> Create(){ 
     ///recover the strings using my stream reader... 
   }
}

I know this approach would work, but I was wondering if it breaks the Factory Pattern to pass parameters to the factory's constructor so I wouldn't break my interface. Are there any counterparts to doing this? Is there another solution I didn't think of? Am I asking too many questions!?! (Yeah, I know the answer to this one!)

Britannia answered 8/10, 2013 at 2:53 Comment(0)
G
4

Parameter in constructor, and constructor itself, should only do one and only one job to do: that is registering dependency. Sometimes, it is needed to "inject" the dependency to factory, as described at abstract factory pattern by Mark Seeman in this answer.

public class ProfileRepositoryFactory : IProfileRepositoryFactory
{
    private readonly IProfileRepository aRepository;
    private readonly IProfileRepository bRepository;

    public ProfileRepositoryFactory(IProfileRepository aRepository,
        IProfileRepository bRepository)
    {
        if(aRepository == null)
        {
            throw new ArgumentNullException("aRepository");
        }
        if(bRepository == null)
        {
            throw new ArgumentNullException("bRepository");
        }

        this.aRepository = aRepository;
        this.bRepository = bRepository;
    }

    public IProfileRepository Create(string profileType)
    {
        if(profileType == "A")
        {
            return this.aRepository;
        }
        if(profileType == "B")
        {
            return this.bRepository;
        }

        // and so on...
    }
}

It is valid in that case, but not in your case because:

  1. It makes your factory have state
  2. It makes your factory more flexible if the parameter (stream) injected as method parameter

    public class StringsListFromFile : IStringsListFactory{
    
       public List<string> Create(StreamReader sr){ 
         ///recover the strings using my stream reader... 
       }
    }
    
  3. If your interface should be flexible for the input, use generic instead

  4. additionally, it is better to return IEnumerable<string> instead of List<string>
Gnni answered 8/10, 2013 at 5:43 Comment(3)
Why does it make my factory have a state since my member is enclosed within my class? And is returning IEnumerable a performance concern?Britannia
Using my design at point 2, you can instantiate your class once, and then using different StreamReader without having to create another object of the same class. It is another story, if your injected StreamReader is a kind of global configuration. But it need another design for better achievement.Gnni
Returning IEnumerable is just a habit of doing Program to an interface rather than program to implementation. Having interface returning IEnumerable can make it also returns array, or other collection such as ObersvableCollection.Gnni
H
0

You could abstract away the implementation of whatever retrieves it. I would also personally pass it through into the method instead of the constructor:

public interface IFactoryDataSourceProvider<T> {
    IList<T> GetData();
}

public class IStringListFactory {
    public IList<string> Create(IFactoryDataSourceProvider<string> provider) {
        return _provider.GetData();
    }
}

Then perhaps:

class StreamReaderDataProvider : IFactoryDataSourceProvider<string> {
    public IList<string> GetData() {
        using (var streamReader = new StreamReader( ... )) {
            return streamReader.ReadAllLines(); // etc.
        }
    }
}

var list = factory.Create(new StreamReaderDataSourceProvider());

This all seems silly for such a small sample.. but I assume this isn't quite as small as your example is.

Hydra answered 8/10, 2013 at 3:3 Comment(2)
In this situation, where would the StreamReader get his constructor's arguments? I really think the DataSourceProvider is a good idea but I'm not sure to fully understand it in this case, maybe because my example is too small indeed.Britannia
That depends entirely on your current setup. Without knowing more it's hard to say. Generally you would pass it through the constructor though.. where you get it from beforehand though is up to the implementation of code higher up.Hydra
S
0

Factory Pattern forces you to use Default Constructor. Using a parametric constructor violates the idea of using factory pattern since the object will not return the valid state to the caller class. In your case, you have to initialize them after the factory class call. This will duplicate your code and the idea of using the factory pattern is to avoid the code duplication. But again I am not familiar with the whole scenario. But as per your set up shown here you should use a method in place of Parametric Constructor.

Sit answered 8/10, 2013 at 4:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.