I 'discovered' interfaces and I started to love them. The beauty of an interface is that it is a contract, and any object that fulfills that contract can be used wherever that interface is required.
The problem with an interface is that it can't have a default implementation, which is a pain for mundane properties and defeats DRY. This is also good, because it keeps the implementation and the the system decoupled. Inheritance, on the hand, maintains a tighter coupling, and has the potential of breaking encapsulation.
Case 1 ( Inheritance with private members, good encapsulation, tightly coupled)
class Employee
{
int money_earned;
string name;
public:
void do_work(){money_earned++;};
string get_name(return name;);
};
class Nurse : public Employee:
{
public:
void do_work(/*do work. Oops, can't update money_earned. Unaware I have to call superclass' do_work()*/);
};
void HireNurse(Nurse *n)
{
nurse->do_work();
)
Case 2 (just an interface)
class IEmployee
{
virtual void do_work()=0;
virtual string get_name()=0;
};
//class Nurse implements IEmployee.
//But now, for each employee, must repeat the get_name() implementation,
//and add a name member string, which breaks DRY.
Case 3: (best of both worlds?)
Similar to Case 1. However, imagine that (hypothetically) C++ did not allow overriding methods except those methods that are pure virtual.
So, in Case 1, overriding do_work() would cause a compile-time error. To fix this, we set do_work() as pure virtual, and add a separate method increment_money_earned(). As an example:
class Employee
{
int money_earned;
string name;
public:
virtual void do_work()=0;
void increment_money_earned(money_earned++;);
string get_name(return name;);
};
class Nurse : public Employee:
{
public:
void do_work(/*do work*/ increment_money_earned(); ); .
};
But even this has problems. What if 3 months from now, Joe Coder creates a Doctor Employee, but he forgets to call increment_money_earned() in do_work()?
The question:
-
Is Case 3 superior to Case 1? Is it because it of 'better encapsulation' or 'more loosely coupled', or some other reason?
-
Is Case 3 superior to Case 2 because it conforms with DRY?
Best Answer
One way to solve the forgetting-to-call-the-superclass problem is to give the control back to the superclass! I've re-jiggered your first example to show how (and made it compile ;)). Oh, I also assume that
do_work()
inEmployee
was supposed to bevirtual
in your first example.Now
do_work()
cannot be overridden. If you want to extend it you have to do it viaon_do_work()
whichdo_work()
has control over.This, of course, can be used with the interface from your second example as well if
Employee
extends it. So, if I understand you correctly I think that makes this Case 3 but without having to use hypothetical C++! It's DRY and it has strong encapsulation.