I \'discovered\' interfaces and I started to love them. The beauty of an interfa
ID: 652434 • Letter: I
Question
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?
Explanation / 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() in Employee was supposed to be virtual in your first example.
#include <string>
using namespace std;
class Employee
{
int money_earned;
string name;
virtual void on_do_work() {}
public:
void do_work() { money_earned++; on_do_work(); }
string get_name() { return name; }
};
class Nurse : public Employee
{
void on_do_work() { /* do more work. Oh, and I don't have to call do_work()! */ }
};
void HireNurse(Nurse* nurse)
{
nurse->do_work();
}
Now do_work() cannot be overridden. If you want to extend it you have to do it via on_do_work() which do_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.
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.