J'ai «découvert» les interfaces et j'ai commencé à les aimer. La beauté d'une interface est qu'elle est un contrat, et tout objet qui remplit ce contrat peut être utilisé partout où cette interface est requise.
Le problème avec une interface est qu'elle ne peut pas avoir d'implémentation par défaut, ce qui nuit aux propriétés banales et bat DRY. C'est également bon, car cela maintient la mise en œuvre et le système découplés. L'hérédité, en revanche, maintient un couplage plus étroit et a le potentiel de briser l'encapsulation.
Cas 1 (héritage avec des membres privés, bonne encapsulation, étroitement couplé)
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();
)
Cas 2 (juste une 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.
Cas 3: (le meilleur des deux mondes?)
Similaire au cas 1 . Cependant, imaginez que (hypothétiquement) C ++ n'autorise pas les méthodes de substitution, sauf les méthodes qui sont purement virtuelles .
Ainsi, dans le cas 1 , la substitution de do_work () entraînerait une erreur de compilation. Pour résoudre ce problème, nous définissons do_work () comme purement virtuel et ajoutons une méthode distincte increment_money_earned (). Par exemple:
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(); ); .
};
Mais même cela a des problèmes. Et si dans 3 mois, Joe Coder crée un médecin employé, mais il oublie d'appeler increment_money_earned () dans do_work ()?
La question:
Le cas 3 est-il supérieur au cas 1 ? Est-ce parce qu'il s'agit d'une «meilleure encapsulation» ou d'un «couplage plus lâche», ou pour une autre raison?
Le cas 3 est-il supérieur au cas 2 car il est conforme à SEC?