You are coming back from grocery shopping and have a walk through the park, you see some animals like a dog, a cat, and two ducks. All of them are drinking at the nearby pond. While you walking out of the park, you wondered: "The cat looked rather old, why is it not dead?". Anyway, you moved on and went to the ATM and get some money. You press the "Withdraw Money" button, enter your pin and finally get the money.
Now what does that have to do with interfaces or abstract classes? Well in our example the animal is the abstract class. We know that animals have some common behaviors. For example, they are alive at a certain age. They drink water and eat food. So all animal share some common behavior and properties. You asked yourself why is the cat not dead. Well all of them have an age but that doesn't mean that all animals die at the same time. All of them sleep, but a dog or a cat sleeps a different amount of time. So what an abstract class is, is just a common set of behavior they share. But sure, the nitty gritty details can vary between them. Another way of seeing that is: "Hey that is an animal and there is no way of being more specific."
And the interface? Look at the ATM. You wanted to withdraw money. So you and the ATM have kind of an operational contract. You want money, and the ATM wants your PIN. You don't care how the ATM withdraws your money. Whether or not a person sits inside the ATM and checks online if you have enough funds or if there is a big SQL database is in the background... you don't care. You care that the operation is executed. And that is all: An interface is just a contract that defines which operations an object performs.
abstract
class
Defining an abstract class can be done like this:
public abstract class Animal
{
// protected set means every derived class can set the value
public int Age { get; protected set; }
// private set means only the abstract Animal class is allowed to set the value
public bool IsDead { get; private set; }
// As every animal drinks water differently, we can't define a common way of drinking
public abstract void DrinkWater();
// We can also define a function as virtual
// virtual means every derived class CAN override that method but does not have to
public virtual void Move() { ... }
// The function is not virtual so no one can override it
// Also it is protected so only Animal or every derivate can call that function
// but not the outside world
protected void Die() { IsDead = true; }
}
public class Dog : Animal
{
// We HAVE TO override that method because in "Animal" it is abstract meaning
// it has 0 logic
public override void DrinkWater() {...}
// You see, no Move method here. We don't have to override
// as there is already a template implementation in place
}
public class Duck : Animal
{
// We HAVE TO override that method because in "Animal" it is abstract meaning
// it has 0 logic
public override void DrinkWater() {...}
// This time we want to override the Move method
// And if it moves, it dies :D
public override void Move() => Die();
}
So we can see a lot of variety in what we can do with abstract
classes. You can see it from that angle: If we have kind of a template, which needs here and there concrete details, then we are speaking most likely of an abstract class.
One class can only implement one abstract class but "unlimited" interfacesWell there are technical limitations. Your compiler will run out of stack and heap memory as it has to build that extremely deep or wide dependency tree..
Now you could say: "Hey Steven, do I need the abstract
part here? I can just use a normal "class", can't I?. Well, something along the line of this:
public class Animal
{
public int Age { get; protected set; }
public bool IsDead { get; private set; }
// As abstract is not allowed inside a non abstract class
// we have to find another way
public virtual void DrinkWater()
{
// Or just return???
throw new NotImplementedException();
}
}
That is bad for a multitude of reasons.
- Now everyone can instantiate an animal like this:
var animal = new Animal();
. That did not work with the abstract class. Now we say: Hey, animal is something very concrete! - No one is forced to implement
DrinkWater
anymore. Well that's bad. - That brings us to the
NotImplementedException
or even if we just return nothing. We shifted from a compile time error to a runtime error. That is in 99.9% of cases bad! - As mentioned in my SOLID article, we violate here the Liskov Substitution Principle.
So just don't.
interfaces
Interfaces are just a contract. Let's say we have a repository to save some text-data. As a consumer do we care where it is saved. If we call save and afterwards load, we are just interested that the stuff is there again. So interfaces are just a contract on which operations someone or something can do, with an expected and well-defined input and output. That is it.
interface IRepository
{
// Everything has to be public, therefore we need no visibility modifier
int SaveText(string text);
string LoadText(int identifier);
}
Now in the background could be a SQL-Database, or a Document Database or a simple text file. It doesn't matter from the outside world.
When should I use what?
As a rule of thumb, parameters of functions should use interfaces. SOLID Principles told you that. But on a serious note: There are multiple reasons:
abstract class
es are normally implementation details. The external world should not rely on implementation details.- As a lot of classes can implement an interface, you can freely switch out the class without changing your method. Hello Open-Close principle.
abstract class
es tend to couple your code together a lot! They make it also harder to unit test in a lot of cases.abstract class
es are meant for sharing internal behavior or state of different objects which represent a is-a relationship.
Short: Abstract classes are used for Modelling a class hierarchy of similar looking classes (For example Animal can be abstract class and Human , Lion, Tiger can be concrete derived classes)
AND
Interface is used for Communication between 2 similar / non similar classes which does not care about type of the class implementing Interface(e.g. Height can be interface property and it can be implemented by Human , Building , Tree. It does not matter if you can eat , you can swim you can die or anything.. it matters only a thing that you need to have Height (implementation in you class) ).
You can also mix that:
public interface IFly
{
void MoveUp();
void MoveDown();
}
public abstract class Bird : IFly
{
public virtual void MoveUp() => FlapWings();
public abstract void MoveDown();
}
public class Duck : Bird
{
public override void MoveDown() => AntiFlapWings();
}
Now if we have a method which wants IFly
we can use Duck
:
var duck = new Duck();
IBelieveICanFly(duck);
void IBelieveICanFly(IFly fly)
{
// ...
}
Bonus: Default interface methods
Since C# 8 you can also define a template body on interface level like that:
IHelloWorldPrinter printer = new HelloWorldPrinter();
printer.PrintHelloWorld(); // Prints Hello World
HelloWorldPrinter anotherPrinter = new HelloWorldPrinter();
anotherPrinter.PrintHelloWorld(); // Compiler error
public interface IHelloWorldPrinter
{
void PrintHelloWorld() => Console.Write("Hello World");
}
public class HelloWorldPrinter : IHelloWorldPrinter { }
So it is kind of an abstract class and blurs the line a lot. There are two options:
- Either you implement the body and then you can use it on class level
- Or you can only use the body if you work on the interface type
Now my personal opinion: I am not a big fan of that feature. I don't see any big usage for it and makes the usage of abstract class
and interface
more complicated than it helps.
Conclusion
interface
s are operational contracts that help you achieve loose coupling. abstract class
es on the other side help you to create a common set of shared behaviour for a set of subclasses. Abstract classes try to reduce code duplication in objects which represent a is-a relation.