什么是平行类层次
如以下UML图所示。Animal都需要吃(eat)食(Food), Dog只吃狗食(DogFood), Cat只吃猫食(CatFood).
狗吃猫食?
Dog dog;
CatFood catFood;
dog.eat(catFood); // 狗吃了猫食。
解决方案
方案1:覆盖(override)eat函数
覆盖函数可定义如下:
void Dog::eat(const Food& food)
{
If (food is NOT an instance of DogFood)
狗吠 and return;
……
}
优点:狗不会误食猫食了。
缺点:只有在狗开始吃时(运行时)才知道食物是否可吃。在某些情况下,为时已晚。
方案2:添加额外接口
为Dog和Cat添加额外接口来吃食,如下图所示。从今以后Dog只能通过eatDogFood接口来吃食,Cat只能通过eatCatFood接口来吃食。
优点:狗吃得安心了,不再担心被喂猫食。
缺点:在Dog和Cat中个增加了一个接口,从而增加了记忆和使用的负担。
方案3:Type trait
我们可以定义一个模板函数(template function),主人一律通过此模板函数来给Animal喂食,而无需分别对待具体Animal,如猫、狗等。模板函数可定义如下:
template<class AnAnimal, class AFood>
void feed(const AnAnimal& animal, const AFood& food)
{
animal.eat(food);
}
显然需要对以上模板参数AnAnimal和AFood进行一定的约束,才能满足我们的需求。首先,AnAnimal必须是一种真正的Animal (继承于Animal)。一个简单赋值可达到目的。赋值语句如下:
const Animal& realAnimal = animal; // 编译错误如果animal不是继承于Animal.
其次,AFood必须是AnAnimal对应的食物类型, 即Animal 对应 Food, Dog对应DogFood, Cat对应CatFood. 换句话说,AFood由AnAnimal来决定,AFood可以看成是AnAnimal的一个特性。这不正是type trait所做的是吗!定义type trait如下:
template<class AnAnimal> struct AnimalFoodTrait;
template<> struct AnimalFoodTrait<Animal> {typedef Food AFood;};
template<> struct AnimalFoodTrait<Dog> {typedef DogFood AFood;};
template<> struct AnimalFoodTrait<Cat> {typedef CatFood AFood;};
最后,利用这些type trait,我们可以修改那个模板函数,让其只带一个AnAnimal模板参数,AFood参数由AnAnimal推导出。
所以,最终模板函数定义为:
template<class AnAnimal>
void feed(const AnAnimal& animal, const AnimalFoodTrait<AnAnimal>::AFood& food)
{
const Animal& realAnimal = animal;
realAnimal.eat(food);
}
利用这个修改过的模板函数,主人立刻(编译期)就可以知道是否喂错了食。以狗吃猫食为例:
Dog dog;
CatFood catFood;
feed(dog, catFood); // 狗吠,CatFood不能转化成DogFood.