Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency injection with abstract class

I am struggling for last two days to get a grip of DI.

I have two problems:

  1. If I have a some common functionality why I can't do the same thing implementing DI with an abstract class?
  2. In my example I have many class instance created under writefile, so should I move out all object creation from there? What if I have a layered design? Should these classes be passed all along?

    public interface IWriteFile
    {
        void write();
    }
    
    public class WriteXMLFile : IWriteFile
    {    
        public void write()
        {
    
        }
    }
    
    public class writefile
    {
        IWriteFile _file;
    
        public writefile(IWriteFile file)
        {
            _file = file;
        }
    
        public void WriteMyFile()
        {
            _file.write();
        }
    }
    
like image 549
ANURAG NIGAM Avatar asked Dec 04 '14 20:12

ANURAG NIGAM


1 Answers

Regarding your first point:

Let's say you want to test something that uses your abstract class. If you're directly using an abstract class, then if you want to do something different in the shared functionality in the common class, there's not much you can do.

With an interface, you've broken the coupling with the dependency. This is what the Dependency Inversion Principle in SOLID is talking about when it says "High-level modules should not depend on low-level modules. Both should depend on abstractions". Your class shouldn't depend on the lower-level class (even if it's an abstract class) to get its work done. Both the lower-level and higher-level class should depend on the interface which defines how they are going to interact. Using an interface helps separate the two, giving you loose coupling. Something else can handle wiring them together.

Regarding your second point:

Powerful DI frameworks actually handle a lot of this complexity for you.

Let's say you have a class - Thing - that has a bunch of dependencies.

public Thing(
    IThingRepository repository, 
    IEmailer emailer, 
    ILogger logger, 
    INeedMoreStuff stuff
) : IThing

When you set up your IoC container, you'd do something like the following to associate which implementation to use for each interface:

IoC.Register<MySqlThingRepository>().As<IThingRepository>();
IoC.Register<MicrosoftExchangeEmailer>().As<IEmailer>();
IoC.Register<TruncatingFileLogger>().As<ILogger>();
IoC.Register<MoreStuff>().As<INeedMoreStuff>();
IoC.Register<Thing>().As<IThing>();

Then you can simply do:

IThing thing = IoC.Resolve<IThing>();

These fancy frameworks handle getting all the dependencies for you (since you told it how to get them) and will construct a Thing for you.

That being said, sometimes a fancy framework like that is overkill. In those cases, I usually use the factory pattern to abstract out the details of creating an object while still making sure the object being created follows good DI principles like constructor injection of its dependencies.

Regarding your polymorphism and interfaces:

Someone described polymorphism as simply "Same interface, different implementation. Substitutability". Wikipedia defines it as "the provision of a single interface to entities of different types". According to those definitions, I think using interfaces doesn't go against polymorphism. I feel in C# interfaces are usually sufficient for providing different implementations of the same "interface".

Yes, you can use an abstract base class to do the same thing, but I'd only use that if there's a common set of functionality I can factor out into the base class to keep my code DRY.

Regarding multiple implementations for an interface with StructureMap

I don't how StructureMap handles this, but presumably there's some logic that will decide when you want to use Thing1 versus Things2. You might be able to provide a delegate to StructureMap that it can use to get an IThing which would contain that logic.

Alternatively, you can have a factory that takes some parameters and decides which IThing to return. There are situations where I've put an interface on my factory and constructor injected that, or alternatively have the factory creating the object using IThing use the ThingFactory to get the right one.

like image 68
Jeff B Avatar answered Oct 06 '22 15:10

Jeff B