Принцип замещения Лискова с множественным наследованием
Я пытаюсь придумать объектно-ориентированный дизайн и испытываю трудности с удовлетворением принципа подстановки Лискова. Вот иллюстративный пример:
class Food
{
public:
virtual void printName() {
//......
}
};
class Fruit : public Food
{
};
class Meat : public Food
{
};
class Animal
{
public:
Food *_preferredFood;
virtual void setFoodPreference(Food *food)=0;
};
class Carnivore: public Animal
{
public:
void setFoodPreference(Food *food) {
this->_preferredFood = dynamic_cast<Meat *>(food);
}
};
class Herbivore: public Animal
{
public:
void setFoodPreference(Food *food) {
this->_preferredFood = dynamic_cast<Fruit *>(food);
}
};
Как я могу применить следующее:
- Каждый подкласс Animal должен позволять устанавливать предпочтения в еде, не нарушая LSP
- Пищевые предпочтения для каждого производного класса животных являются подклассом продуктов питания.
Например, если кто-то расширяет Animal
создавать MarineMammal
предпочтение в еде может быть Fish
(которые они будут создавать путем расширения Food
).
1 ответ
Когда Carnivore::setFoodPreference
только принимает Meat
а также Herbivore::setFoodPreference
только принимает Fruit
, то они не соответствуют одному и тому же договору. Это означает, что они на самом деле не тот же метод. Когда вы вызываете этот метод, вы должны знать, имеете ли вы дело с хищником или травоядным, чтобы избежать передачи неправильного типа. Когда вы забудете проверить это, вы рискуете создать ошибку, которая проявляется в форме ошибки приведения во время выполнения.
Решение состоит в том, чтобы разделить эти два метода.
Я бы порекомендовал вам удалить setFoodPreference
из открытого интерфейса и вместо этого добавить методы Carnivore::setMeatPreference(Meat *meat)
а также Herbivore::setFruitPreference(Fruit *fruit)
непосредственно к подклассам. Таким образом, любой код, который устанавливает предпочтение пищи, должен знать, с каким типом Животного и с каким продуктом он имеет дело, так что вы больше не сможете писать код, который пытается установить несовместимый тип пищи.
Внутренне оба метода могут установить protected Food *_preferredFood
из общего базового класса. Или даже лучше, позвоните protected void setPreferredFood(Food *food)
который является сеттером для private Food* _preferredFood
, Эта переменная определенно не должна быть публичной, чтобы обеспечить правильную инкапсуляцию.