Ich 'entdeckte' Schnittstellen und fing an, sie zu lieben. Das Schöne an einer Schnittstelle ist, dass es sich um einen Vertrag handelt und jedes Objekt, das diesen Vertrag erfüllt, überall dort verwendet werden kann, wo diese Schnittstelle erforderlich ist.
Das Problem mit einer Schnittstelle ist, dass sie keine Standardimplementierung haben kann, was für alltägliche Eigenschaften schmerzhaft ist und DRY besiegt. Dies ist auch gut, weil dadurch die Implementierung und das System entkoppelt bleiben. Die Vererbung hingegen hält eine engere Kopplung aufrecht und kann die Einkapselung unterbrechen.
Fall 1 (Vererbung mit privaten Mitgliedern, gute Kapselung, eng gekoppelt)
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();
)
Fall 2 (nur eine Schnittstelle)
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.
Fall 3: (das Beste aus beiden Welten?)
Ähnlich wie in Fall 1 . Stellen Sie sich jedoch vor, dass C ++ (hypothetisch) keine überschreibenden Methoden außer den rein virtuellen Methoden zuließ .
In Fall 1 würde das Überschreiben von do_work () einen Fehler bei der Kompilierung verursachen. Um dies zu beheben, setzen wir do_work () als rein virtuell und fügen eine separate Methode increment_money_earned () hinzu. Als Beispiel:
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(); ); .
};
Aber auch das hat Probleme. Was ist, wenn Joe Coder in 3 Monaten einen Doctor Employee erstellt, aber vergisst, increment_money_earned () in do_work () aufzurufen?
Die Frage:
Ist Fall 3 Fall 1 überlegen ? Liegt es an einer „besseren Einkapselung“ oder einer „lockereren Kopplung“ oder an einem anderen Grund?
Ist Fall 3 Fall 2 überlegen, weil er mit DRY übereinstimmt?