Search code examples
c#design-patternsdependency-injectioninversion-of-controlloose-coupling

How should concrete types return an instance of an interface in a loosely-coupled way?


I have an IDirectory interface and a Directory class that implements it.
I need to create an IDirectoryEnumerator which just has a method that looks like so:

IEnumerable<IDirectory> GetDirectories();

So I created a DirectoryEnumerator class, but I'm not sure which IDirectory to return in GetDirectories(), since I want to keep my code loosely-coupled.

Should I use generics with constraints to IDirectory somehow?
Should I get it somehow through a DI container?
Should something like that be tightly-coupled and I should focus on a specific concrete type?
Or is there another better option?

Note: concrete types of IDirectory in my example don't have any default constructors and are immutable.


Solution

  • You could let the concrete IDirectoryEnumerator implementation accept some sort of factory (e.g. IDirectoyFactory) through the constructor, and then use a parametrized method in that factory to create concrete types of IDirectory.

    This also makes testing easier and enables you to use a DI container to inject the dependencies.

    EXAMPLE:

    public interface IDirectoryEnumerator
    {
        IEnumerable<IDirectory> GetDirectories();
    }
    
    public class DirectoryEnumImpl : IDirectoryEnumerator
    {
        private readonly IDirectoryFactory _directoryFactory;
        
        public DirectoryEnumImpl(IDirectoryFactory directoryFactory)
        {
            _directoryFactory = directoryFactory;
        }
    
        public IEnumerable<IDirectory> GetDirectories()
        {
            // you can use the factory here
        }
    }
    
    public interface IDirectoryFactory
    {
        IDirectory CreateDirectory(DirectoryType directoryType);
    }
    
    public class DirectoryFactoryImpl : IDirectoryFactory
    {
        IDirectory CreateDirectory(DirectoryType directoryType)
        {
            if (directoryType == DirectoryType.DIR_A)
                return new Dir_A();
    
            // the same goes for other types.
        }
    }
    
    public enum DirectoryType 
    {
        DIR_A, DIR_B, // etc ...
    }