10

I am beginning my journey of learning about dependency injection, and one of the reasons that I saw why it is a good idea to use DI was that it explicitly specifies your dependencies, which also makes your code more clear.

I have also noticed that interfaces are used abundantly, but I want to know why would we not use abstract classes for the sole purpose of specifying a default constructor?

Of course no implementation could be included in the abstract class.

Wouldn't this:

abstract class FooBase
{
    protected IBar _bar;

    FooBase(IBar bar)
    {
        _bar = bar;
    }

    abstract void DoSomething();
    abstract void DoSomethingElse();
}

Demonstrate more clearly what the dependency of a FooBase object is more than:

interface IFoo
{
    IBar Bar { get; }

    void DoSomething();
    void DoSomethingElse();
}

?

Please keep in mind I am new to this whole concept so be nice :)

ChandlerPelhams
  • 1,648
  • 4
  • 38
  • 56
  • 6
    Interfaces are more flexible, in that any class can implement an additional interface. But any given class can only have one base class. – Matt Greer Jul 19 '12 at 21:24
  • 1
    Why could there be "no implementation in the abstract class"? – Austin Salonen Jul 19 '12 at 21:24
  • @AustinSalonen I believe because with DI we want to promote "loose coupling" and ease of testing. Implementation in the abstract class would make it difficult to swap out other objects that have different logic than the abstract class. – ChandlerPelhams Jul 19 '12 at 21:26
  • @MattGreer Does this trump the clarity of having a parameterized default constructor in an abstract class? – ChandlerPelhams Jul 19 '12 at 21:27
  • 2
    Side note on @MattGreer comment: C#/Java are not the only languages where DI can be used, but but for C# using base class is very restrictive due to single base class requirement. – Alexei Levenkov Jul 19 '12 at 21:29
  • I made the comment because I find it very strange when people artificially prohibit usage of a core concept. An abstract class isn't _automatically_ tightly coupled just because it provides common behavior to its descendants. Whether or not it's appropriate for your scenario is up to you but the blanket statement of "no implementation in abstract base classes" is a pointless restriction. – Austin Salonen Jul 19 '12 at 21:37
  • @AustinSalonen Good point. I guess I shouldn't say NO implementation, but instead minimal implementation. – ChandlerPelhams Jul 19 '12 at 21:40

3 Answers3

6

One technical reason - forcing particular parent class in languages without multiple inheritance (Java/C#) will significantly restrict freedom of implementation of the "interface".

Note that there are 2 concepts hidden behind "interface" word and it sort of make it harder to reason in C#:

  • "interface" and abstract concept: well defined set of properties/methods to interact with an object; contract to work with an object.
  • "interface" as type in particular language (C#/Java) - one possible representation of contract in given language.

Abstract/concrete classes can be used to represent contract, but force restrictions on implementers of contract in C#.

Some other languages (like C++) don't have such restriction and abstract classes is good option there. Other languages (i.e. "duck-types" JavaScript) does not have such class/interface distinction, but you can still have "contract" with an object.

Sample:

To provide more context where you should be hitting this restriction yourself in C#: DI is commonly used along with some form of TDD or at least with basic unit tests. So you try write some code and tests that uses abstract base class for DI.

abstract class Duck { 
  abstract void Quack();
}
class ConcreteDuck : Duck {...}

Now if you decide to write tests you may already have test classes that helps you to mock objects (if you are not using existing once)

class MockBase {...}

class MockDuck : MockBase,?????? // Can't use Duck and MockBase together...
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
5

Interface defines a contract. An Abstract base class defines a behavior.

Essentially, you can provide a single class that implements multiple interfaces, which then in turn can be injected into multiple classes, but you will only have a single abstract base class (at least in C#).

Consider the point of registering a type at the container (the composition root at best) and consider the point where you resolve the dependency (the constructor or a property).

This SO will cover some more aspects SO on interface vs base class

Community
  • 1
  • 1
Mare Infinitus
  • 8,024
  • 8
  • 64
  • 113
1

In .NET, you only have single inheritance. In languages/frameworks where this is the case, opting to use an abstract class as your abstraction gives the potential to "burn the base class".

This means that you force the implementer of your abstraction to inherit from a singular class, when forcing them to do so might result in inconveniencing them severely, depending on what the implementation is.

Let's say that you have your abstract class Contract. If someone has their own Base class that they want to use which exposes only protected methods (for inheritors).

Because the methods are protected, one can't use encapsulation (an instance of Base stored in a field) to access the methods in Base for your abstraction implementation.

Even worse, if you don't have access to modify Base, then you might have to resort to some very ugly workarounds (Reflection, namely).

That said, with interfaces, you give the implementer the choice of where to inherit from and don't limit their options.

The typical pattern you'll see is that you always provide an interface for your contract and code your consumers against the interface. You also provide an abstract base class that provides functionality that people may derive from for convenience, but are not obligated to derive from.

Also, if it's possible for you to provide this functionality in the form of something that is easily encapsulated (for the condition I describe above), it would be even more optimal (you'd have an abstract class which just calls the instance that exposes the methods).

casperOne
  • 73,706
  • 19
  • 184
  • 253