Раньше я считал, что при «правильном» дизайне всегда можно избежать удручения. Это просто не тот случай, хотя. Правильный дизайн очень часто должен иметь подобъекты, которые реализуют новое, а не просто другое поведение. Слишком часто сторонники «правильного» дизайна скажут вам переместить новое поведение вверх по стеку абстракции в места, где оно не принадлежит. Не всегда, но если вы продолжаете пытаться убедиться, что все ваши классы могут быть использованы с самого абстрактного момента, это очень часто, когда все заканчивается, и это просто бесполезно.
Один из отличных способов централизованного управления даункастингом - это использование шаблона посетителя. Есть несколько форм посетителей, но некоторые требуют уныния, а некоторые нет. С ациклическим посетителем, для которого требуется снижение рейтинга, с ним легче работать, и, по моему опыту, он более мощный.
Другой посетитель, с которым я не пытался работать с претензиями, чтобы удовлетворить ту же гибкость ациклического посетителя, что и скорость стандартного посетителя; это называется "кооперативный посетитель". Он все еще выполняется, он просто делает это быстрее с помощью своей таблицы поиска. Причина, по которой я не попробовал кооперативного посетителя, заключается в том, что я не нашел способа заставить его работать на нескольких высших иерархиях ... но я тоже не тратил на это много времени, потому что застрял ( Текущий проект) с ациклическим.
Самое классное в кооперативном посетителе - это типы возврата. Тем не менее, я использую своих посетителей, чтобы посещать целые блоки объектов и делать что-то с ними. У меня есть проблема с представлением, как возврат будет работать в этих случаях.
Стандартный посетитель также понижает рейтинг и делает это с помощью механизма виртуальных вызовов, который быстрее и иногда безопаснее, чем явное приведение. Что мне не нравится в этом посетителе, так это то, что если вам нужно посетить WidgetX в высокоуровневой иерархии виджетов, вам также необходимо реализовать функцию visit () для WidgetY и WidgetZ, даже если вас это не волнует. С большими и / или широкими высшими артериями это может быть PITA. Другие варианты не требуют этого.
Существует также «высший иерархический посетитель». Он знает, когда выйти.
Если вы не склонны использовать посетителя и хотите просто выполнить приведение, то вы можете рассмотреть возможность использования функции boost :: polymorphic_downcast. Он имеет механизмы безопасности и предупреждения динамического приведения с утверждениями в отладочных сборках, а также скорость статического приведения в выпуске. Это не может быть необходимым, хотя. Иногда вы просто знаете, что правильно произносите.
Важная вещь, о которой вам нужно подумать и чего вы хотите избежать, это сломать LSP. Если у вас есть много кода с помощью if (widget-> type () == type1) {downcast ...} else if (widget-> type () == type2) ... ", то добавление новых типов виджетов это большая проблема, которая плохо влияет на большую часть кода. Ваш новый виджет на самом деле не будет виджетом, потому что все ваши клиенты слишком близки к вашей высокой иерархии и не знают об этом. Шаблон посетителя не избавляет от этой проблемы, но он централизуется, что очень важно, когда у вас неприятный запах, и часто облегчает работу с ним.