Let's take your example of a Dog and a Cat class, and let's illustrate using C#:
Both a dog and a cat are animals, specifically, quadruped mammals (animals are waaay too general). Let us assume that you have an abstract class Mammal, for both of them:
public abstract class Mammal
This base class will probably have default methods such as:
All of which are behavior that have more or less the same implementation between either species. To define this you will have:
public class Dog : Mammal
public class Cat : Mammal
Now let's suppose there are other mammals, which we will usually see in a zoo:
public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal
This will still be valid because at the core of the functionality Feed()
and Mate()
will still be the same.
However, giraffes, rhinoceros, and hippos are not exactly animals that you can make pets out of. That's where an interface will be useful:
public interface IPettable
{
IList<Trick> Tricks{get; set;}
void Bathe();
void Train(Trick t);
}
The implementation for the above contract will not be the same between a cat and dog; putting their implementations in an abstract class to inherit will be a bad idea.
Your Dog and Cat definitions should now look like:
public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable
Theoretically you can override them from a higher base class, but essentially an interface allows you to add on only the things you need into a class without the need for inheritance.
Consequently, because you can usually only inherit from one abstract class (in most statically typed OO languages that is... exceptions include C++) but be able to implement multiple interfaces, it allows you to construct objects in a strictly as required basis.
To expand on the answer by bradtgmurray, you may want to make one exception to the pure virtual method list of your interface by adding a virtual destructor. This allows you to pass pointer ownership to another party without exposing the concrete derived class. The destructor doesn't have to do anything, because the interface doesn't have any concrete members. It might seem contradictory to define a function as both virtual and inline, but trust me - it isn't.
class IDemo
{
public:
virtual ~IDemo() {}
virtual void OverrideMe() = 0;
};
class Parent
{
public:
virtual ~Parent();
};
class Child : public Parent, public IDemo
{
public:
virtual void OverrideMe()
{
//do stuff
}
};
You don't have to include a body for the virtual destructor - it turns out some compilers have trouble optimizing an empty destructor and you're better off using the default.
Best Answer
You achieve to implement a principle that says
This has been introduced by GoF in their seminal book called Design Patterns. The benefits of adhering to such a practice are the following:
Based on the example you have posted we have two interfaces:
The interface
IBankAccount
is composed from two methods and one property that each bank account should have (at least in the perspective of the author of this interface and the problem she/he tried to solve).Specifically, we should be able to deposit money (
PayIn
) and withdraw money (Withdraw
) from a bank account and last but not least to read the account's balanceBalance
.Regarding the second interface,
ITransferBankAccount
this exhibits a behavior that only some accounts may have. This behavior is to transfer money. We could assume that there are two kinds of bank accounts those from which you can transfer money from one bank account to another and those that you can't, you can only deposit/withdraw from that specific bank account. It's just an assumption tied to this specific problem that Author of theses interfaces she/he tried to solve,Even before we proceed to the
CurrentAccount
the benefits of interfaces are evident in the second interface !We define a behavior, those of transferring money and we don't require the
TranferTo
method has as it's first parameter a custom type likeCurrentAccount
or another type. We just require that the first parameter of this method should implement the interfaceIBankAccount
. Just this ! This is enough for us to transfer some money, since we can deposit money to an account by using thePayIn
method.For instance let that we have 3 bank accounts A, B, C. Furthermore, let's assume that the account B and account C are different in terms of business. For instance whenever account B receives an amount of money we should do some extra actions if we compare it with account C. Definetely that means that in order to model this we should have two different classes.
BankAccount_B
andBankAccount_C
. In addition to this let's suppose that whenever we Withdraw money from A to any other account we have to get a specific fee and apply some extra logic. That definitely means that bank account A should be modelled using another class. Let's call itBankAccount_A
.If we suppose that all these classes
BankAccount_A
,BankAccount_B
andBankAccount_C
implement the interfaceIBankAccount
and the classBankAccount_A
implements the interfaceITransferBankAccount
, then your can write code like this:Suppose that now you didn't have followed this principle and you wanted to implement the above functionality. In this case you should have defined two methods in class
BankAccount_A
. One for transferring money to bank accounts that are created using theBankAccount_B
and one another for transferring money to bank accounts that are created using theBankAccount_C
. And what about if you had another requirement for another account D in the future ? You should define another method etc...And this time you should violate one of the principles of SOLID, the Open/Closed principle, software entities … should be open for extension, but closed for modification. Had we used the interface we had been closed for modification :).