Принцип замещения Лискова с множественным наследованием

Я пытаюсь придумать объектно-ориентированный дизайн и испытываю трудности с удовлетворением принципа подстановки Лискова. Вот иллюстративный пример:

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);
    }
};

Как я могу применить следующее:

  1. Каждый подкласс Animal должен позволять устанавливать предпочтения в еде, не нарушая LSP
  2. Пищевые предпочтения для каждого производного класса животных являются подклассом продуктов питания.

Например, если кто-то расширяет 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, Эта переменная определенно не должна быть публичной, чтобы обеспечить правильную инкапсуляцию.

Другие вопросы по тегам