Ho "scoperto" le interfacce e ho iniziato ad amarle. La bellezza di un'interfaccia è che si tratta di un contratto, e qualsiasi oggetto che soddisfi tale contratto può essere utilizzato ovunque sia richiesta tale interfaccia.
Il problema con un'interfaccia è che non può avere un'implementazione predefinita, il che è un problema per le proprietà banali e le sconfitte. Anche questo è positivo, perché mantiene l'implementazione e il sistema disaccoppiati. L'ereditarietà, sulla mano, mantiene un accoppiamento più stretto e ha il potenziale di rompere l'incapsulamento.
Caso 1 (Ereditarietà con membri privati, buon incapsulamento, strettamente accoppiati)
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();
)
Caso 2 (solo un'interfaccia)
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.
Caso 3: (il meglio dei due mondi?)
Simile a Caso 1 . Tuttavia, immagina che (ipoteticamente) C ++ non abbia permesso l'override dei metodi tranne quei metodi che sono pure virtual .
Quindi, in Caso 1 , l'override di do_work () causerebbe un errore in fase di compilazione. Per risolvere questo problema, impostiamo do_work () come puro virtuale e aggiungiamo un metodo separato increment_money_earned (). Ad esempio:
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(); ); .
};
Ma anche questo ha problemi. E se fra 3 mesi, Joe Coder crea un Dipendente Dottore, ma dimentica di chiamare increment_money_earned () in do_work ()?
La domanda:
-
È Caso 3 superiore a Caso 1 ? È dovuto al "migliore incapsulamento" o "più sciolto", o qualche altro motivo?
-
È Caso 3 superiore a Caso 2 perché è conforme a DRY?