سؤال

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!)

هل كانت مفيدة؟

المحلول

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>

نصائح أخرى

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.

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top