目录
继承和白箱复用
简单来说,继承是一种类设计层次的代码复用,继承允许你根据基类的实现来定义派生类的实现。如以下代码:
class fruit
{
public:
void grow()
{}
protected:
string _name;
string _taste;
int _life;
};
class watermelon : public fruit
{};
根据继承的知识可知,watermelon类中会继承fruit类中的成员变量和成员方法,如果fruit类中有成员需要改变,就会影响继承fruit类的子类;同样,当我们使用watermelon类的时候,我们还需要关心fruit类的内部实现;
在软件工程中,这种通过生成派生类的复用称之为白箱复用,意思就是我能看到我复用的类里面的实现细节,并且在使用的时候也需要关注这个被复用的类的实现细节;就好比说,有一个透明的箱子,我去这个透明的箱子中拿东西,我是可以看到箱子里的细节的,并且还需要关注内部的细节。
因此,可以看出,使用继承的两个类,类与类之间高度的耦合在一起,在实际开发中,我们并不希望类与类之间的耦合度过高;因为,继承体系一旦庞大,上层的类一旦改变,很有可能 “牵一发而动全身” 以至于后面的基于该父类的代码都要改变。
组合和黑箱复用
组合是什么呢?组合提供了类与类之间除了继承之外的另一种代码复用的选择。简单来说,组合就是类和类之间组合在一起;举个例子,我们有一个汽车类,还有一个轮胎类,汽车类中需要使用轮胎类的代码,我们就可以将轮胎类的对象声明为汽车类的成元变量。使用组合代码如下:
class Tire
{};
class Car
{
private:
Tire _tire;
};
可以看出, Car这个类中组合了Tire这个类,当我们需要使用Tire类的时候,我们只需要关注其接口即可,不需要关注其内部的实现细节,因此,被组合的类应该提供良好的接口定义,便于被使用。
在软件工程中,这种风格的代码复用被称为黑箱复用;意思就是我们不能看到我复用的类里面的实现细节,并且在使用的时候也需不要关注这个被复用的类的实现细节;就好比说,有一个黑色的箱子,我们不能直接去这个箱子中拿东西,只能使用这个黑色的箱子提供的接口,我们不能看到箱子里的细节的,并且也不需要关注内部的细节。
因此,可以看出,使用组合的两个类,类与类之间并没有高度的耦合在一起,类与类之间是高内聚的;在实际开发中更期望写出 “低耦合,高内聚” 的代码,这样的代码不会产生继承那样 “牵一发而动全身” 的严重后果,哪怕类与类之间有影响,影响也不会特别大。
继承和组合分别在什么时候使用
一个类继承了另一个类,既然能继承,说明他们之间有一定的 “血缘关系”;举个例子,Student类继承Person类,子类肯定和父类之间具有一些公共的特征,才能继承;可以说继承之间的关系是 is a 的关系,相当于,我们可以说Student是一个Person。
一个类组合了另一个类,组合,相当于主体和零件之间才需要组合;举个例子,Car类和Tire类,Car类需要组和Tire类,是主体和零件这样的从属关系;可以说组合之间的关系是 has a 的关系,在该例子中,我们可以说Car中有一个Tire。
继承是一种白箱复用,组合是一种黑箱复用,继承和组合在代码复用上的区别也就是白箱复用和黑箱复用的区别。在类与类之间的关系上的区别,继承是is a的关系,组合是has a的关系。如果类与类之间的关系更符合is a的关系,使用组合更合理,如果类与类之间的关系更符合has a的关系,使用继承更合理。如果类与类之间的关系不明确,优先使用组合。