Вызов метода производного класса из цикла списка базовых указателей (OOD) - PullRequest
0 голосов
/ 22 ноября 2018

Проблема

Я столкнулся с простой проблемой, хотя не могу придумать для нее подходящее OOD.

Что у меня есть:

  • Базовый класс
  • Подкласс, добавляющий новый метод foo()
  • Список указателей на экземпляры базового класса

Что яneed:

Мне нужно перебрать этот список и вызвать foo() для объектов, поддерживающих этот метод, т.е. объектов (или производных от) вышеупомянутого подкласса.Или, вообще говоря, мне нужен «не вонючий» полиморфный доступ к подклассу через список указателей на базовый класс.

Пример кода

class Entity {
    // ...
    // This class contains methods also needed by subclasses.
};

class SaveableEntity : public Entity {
public:
    virtual void save() = 0;
};

// SaveableEntity has multiple subclasses with specific save() implementations.

std::vector<Entity *> list;
for (Entity *entity : list) {
    // Here I need to save() the descendants of a SaveableEntity type.
}

Я пришел с некоторыми идеями,однако ни один из них не кажется мне правильным.Вот некоторые из них:

Метод 1: dynamic_cast

Поскольку некоторые элементы являются сохраняемыми, а некоторые нет, наиболее очевидный способ, который я вижу, - это динамическое приведение:

std::vector<Entity *> list;
for (Entity *entity : list) {
    auto saveable = dynamic_cast<SaveableEntity *>(entity);
    if (saveable) {
        saveable->save();
    }
}

Однако использование dynamic_cast выглядит как плохой OOD в этой ситуации (поправьте меня, если я ошибаюсь).Кроме того, этот подход может легко привести к нарушению LSP .

Метод 2: Переместить save() в базовый класс

Я мог удалить SaveableEntity ипереместите метод save() на базу Entity.Тем не менее, это заставляет нас реализовать фиктивный метод:

class Entity {
    virtual void save() {
        // Do nothing, override in subclasses
    }
};

Это исключает использование dynamic_cast, но фиктивный метод все еще не выглядит правильным: теперь базовый класс содержит информацию (метод save())*

Метод 3. Применение шаблонов проектирования

  • Стратегия шаблон: SaveStrategy класс и его подклассы, такие как NoSaveStrategy, SomeSaveStrategySomeOtherSaveStrategy и т. Д. Опять же, наличие NoSaveStrategy возвращает нас к недостатку предыдущего метода: базовый класс должен знать конкретные детали своего подкласса, что выглядит как плохой дизайн.
  • Добавитьнекоторые слои композиции-наследования и т. д. и т. д. ...

Вопрос

Возможно, я упускаю какое-то очевидное решение или, возможно, описанные методы (1 или2) не так плохо и вонючий в этом конкретном мошенничестветекст, как я их вижу.

Так какой подход к дизайну подходит в такой ситуации?

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Моей первой идеей было бы дополнить базовый класс методом virtual bool trySave().По умолчанию может быть return false;, в то время как SaveableEntity обеспечивает это переопределение:

class SaveableEntity : public Entity {
public:
    virtual bool trySave() final override
    {
        save();
        return true;
    }

    // You could also make this protected.
    virtual void save() = 0;
};

Является ли это более подходящим, чем предложение @ YSC для вашего конкретного случая, вы должны решить для себя.Это похоже на перемещение save() в базовый класс, но значительно менее запутанно для пользователя.

0 голосов
/ 22 ноября 2018

Существует решение № 4, поощряемое ориентированным на данные программированием (об этом было прекрасно сказано в cppcon 2018, доступном на youtube): с двумя списками .Один список предназначен для всех SavableEntity с, а другой для Entity с, которые нельзя сохранить.

Теперь вы перебираете первый список и ->save() эти элементы.

Основным преимуществом является то, что вы перебираете только соответствующие объекты.При некотором (возможно, серьезном) рефакторинге вы можете иметь коллекцию объектов, а не указатели на некоторые.Это увеличит локальность данных и резко сократит количество пропусков кэша.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...