Почему dynamic_cast считается плохой практикой, и когда он действительно необходим?
Я пытаюсь найти доказательства того, что в некоторых случаях dynamic_cast может быть полезен, но информации по этой теме почему-то крайне мало.
Например, недавно я столкнулся с одной ситуацией, над которой очень крепко думал. Давайте рассмотрим ее.
Допустим, в сцене (в игре) имеется система частиц, которая обладает следующими свойствами:
- Все типы частиц наследуются от интерфейса
Particle; - Высока вероятность добавления новых типов частиц;
- Допустим, есть такие типы частиц:
Dust,Blood,Rain,Fair,Shard; - Каждый тип частиц на 90% уникален относительно других типов частиц - отличаются переменные-члены, интерфейс, алгоритм взаимодействия с объектами сцены, а также список объектов, с которыми это взаимодействие происходит;
- По правилам хорошего тона, частица не знает, как она будет отрисовываться, с кем она будет взаимодействовать и прочие подобные вещи.
На мой взгляд, в этом сценарии полиморфизм на основе интерфейса абсолютно не применим. Причина заключается в том, что полиморфизм подтипов через интерфейс предка допустим только в ситуациях, когда потомок сам себя обрабатывает. То есть, когда у потомка есть вся необходимая информация для обработки.
Например, каждая конкретная сцена может сама себя обработать, потому что взаимодействие в основном происходит с тем, что содержится в самой сцене. Поэтому, мы можем управлять любой сценой через обобщенный интерфейс вида: react(event), draw(render), process(dt).
С системой частиц ситуация обстоит точно противоположная. У частицы нет информации о том, где она находится и с чем ей нужно взаимодействовать. Поэтому, система частиц не способна управлять частицами через обобщенный интерфейс.
Вернее, это возможно, но я не понимаю, для чего все эти сложности.
Иногда я поражаюсь, что в коде начинают использоваться крайне запутанные решения на основе того же паттерна проектирования Посетитель(Visitor). Когда я спрашиваю, зачем это было сделано, мне говорят, что это правильно, а вот использование dynamic_cast - это неправильно.
Самое интересное заключается в том, что если в коде использовать dynamic_cast, то код становится понятнее, короче и гибче.
Более того, в некоторых особенно требовательных ко скорости случаях можно вообще обойтись без полиморфизма. Например, использовать обертку, которая хранит ID типа и void*. Тогда вся магия будет происходит лишь в двух местах: в точке доступа к объекту через обертку и в точке обработки объекта, который сокрыт под оберткой.
Объясните мне, пожалуйста, неужели тонны запутанного кода на основе посетителей чем-то лучше, чем использование dynamic_cast-а или его самописного аналога?
Ответы (2 шт):
Когда используется dinamic_cast, то в поля всей иерархии классов добавляется "поле типа". Это не есть хорошо с точки зрения оверхеда. Возможно, что некоторые задачи не могут быть выражены только с помощью нисходящего проектирования. И тогда надо применять dinamic_cast. Но надо понимать, что там есть оверхед и что он занимает время и память.
Не знаю почему вдруг dynamic_cast стал плохой практикой, так как все зависит от задачи, которую необходимо решить.
Представим, что вы разрабатываете программное обеспечение от которого зависят жизни людей и используете полиморфизм с приведением типов. В данном случае следует использовать проверенный и надежный алгоритм, проверенный миллионами людей, а не самописный. Здесь dynamic_cast вне конкуренции, а любая другая реализация его замещающая не только непрофессиональна, но может даже и преступна.
Если вы разрабатываете программное обеспечение от которого не зависят жизни людей и оно критично к производительности. При этом нарушение его работы меньшее зло чем скорость, тогда довольно тяжеловесный dynamic_cast может оказаться препятствием для достижения цели. В этом случае вполне допустимо его замещение на что-то более производительное.
Отсюда ответ на Ваш вопрос. Используйте dynamic_cast где есть высокие требования к безопасности и замещайте его где есть высокие требования к производительности.