Проблема
Я столкнулся с простой проблемой, хотя я не могу придумать для нее подходящего OOD.
Что у меня есть:
- Базовый класс
- Подкласс, добавляющий новый метод
foo()
- Список указателей на экземпляры базового класса
Что мне нужно:
Мне нужно просмотреть этот список и вызвать 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
,SomeSaveStrategy
,SomeOtherSaveStrategy
и т. д. Опять же, наличиеNoSaveStrategy
возвращает нас к недостатку предыдущего метода: базовый класс должен знать конкретные детали о его подкласс, который кажется плохим дизайном. - Шаблоны Proxy или Decorator могут легко инкапсулировать
dynamic_cast
, однако это только скроет нежелательный код, но не избавит от самого плохого дизайна. - Добавьте несколько слоев «композиция вместо наследования» и т. д. и т. д.
Вопрос
Может быть, я упускаю какое-то очевидное решение, а может быть, описанные способы (1 или 2) не так плохи и вонючи в данном конкретном контексте, как я их вижу.
Так какой подход к дизайну подходит в такой ситуации?