我“发现”了界面,并开始喜欢它们。接口的优点在于它是一个契约,任何需要履行该契约的对象都可以在需要该接口的任何地方使用。
接口的问题是它不能具有默认实现,这对于平凡的属性是一种痛苦,并且会破坏DRY。这也很好,因为它可以使实现与系统保持分离。一方面,继承会保持更紧密的耦合,并且有可能破坏封装。
案例1(与私有成员的继承,良好的封装,紧密耦合)
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();
)
情况2(只是一个接口)
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.
情况3 :(两全其美?)
与案例1相似。但是,想象一下(假设),C ++不允许重写方法,只是那些纯virtual方法。
因此,在案例1中,重写do_work()会导致编译时错误。为了解决这个问题,我们将do_work()设置为纯虚拟的,并添加一个单独的方法增量_money_earned()。举个例子:
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(); ); .
};
但这甚至有问题。如果从现在开始的3个月后,Joe Coder创建了一个Doctor Employee,但他忘记在do_work()中调用增量_money_earned(),该怎么办?
问题:
案例3是否优于案例1?是因为“更好的封装”或“更松散地耦合”,还是其他原因?
案例3是否符合DRY,是否优于案例2?