2012-05-25 2 views
2

У меня проблема, которая заставляет меня чувствовать себя довольно глупым. В проекте hobby у меня есть std :: список указателей на класс интерфейса, которые указывают на различные конкретные реализации указанного интерфейса.Подсчет объектов без RTTI

Например, у меня есть следующий:

class Seafood ... 
class Fishstick : public Seafood ... 
class Squid : public Seafood ... 
... 
std::list<Seafood*> buffet; 

Теперь, когда я мой буфет, населенный различными элементы из морепродуктов, я хочу, чтобы подсчитать количество Fishsticks я быть_наст доступный для просмотра, если больше нужно быть приказал с кухни.

Как я могу сделать это без RTTI или какой-либо коварной реализации? Я прочитал несколько статей, которые утверждают, что если вы захотите использовать RTTI, вы приближаетесь к ООП неправильно и/или ваше решение должно быть перепроектировано. Существуют ли какие-то шаблоны или другие решения, которые справляются с этой проблемой? Который я уверен, должен был всплыть много раз прежде.

Я думал о очевидности, которая является своего рода виртуальной функцией, но я не могу понять, как это сделать, не строя в дрянной версии RTTI, или некоторые знания о потомках в интерфейсе (CountIfFishstick/IsFishstick/Есть (тип)).

Редактировать: еще одна вещь, которая пришла на ум, заключалась в том, чтобы сохранить один список рыбных палочек, один список кальмаров и т. Д. Но это наверняка победит всю цель интерфейса/внедрения.

+1

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

+0

Если вы абсолютно должны проверить тип переменной, чтобы определить ее характер, 'bool isFishstick = !! dynamic_cast (значение);' должно работать. Тем не менее, вы должны искать другие подходы к этой проблеме. –

+2

На самом деле это довольно глупо, чтобы все испугались RTTI и т. Д. Запах не указывает на плохой дизайн, это указывает на то, что МОЖЕТ БЫТЬ плохим дизайном. В то время как полиморфизм - это то, что нужно обрабатывать то же самое, иногда необходимо, например, при подсчете конкретных типов, выкапывать в иерархию наследования. Выполнение этого, а не какого-либо другого свернутого предмета - это самый простой и лучший способ сделать это. –

ответ

3

Возможно, вам нужен какой-либо вариант шаблона посетителя. Их много, и трудно сказать, чего вы хотите. Я бы рекомендовал получить Modern C++ Design и пересмотреть реализацию Alexendrescu. В противном случае, google «шаблон посетителя», и вы получите 1000 км ссылок для чтения.

+0

Я могу принять это как ответ за 4 минуты, но да. Эта картина будет работать красиво. Я буду расширять свой вопрос, чтобы ответить на него с вашим предложением. – Piotr

1

Visitor pattern - это то, что вы ищете. Существует также специальная версия посетителя, которая называется Acyclic visitor, которая использует RTTI для решения некоторых проблем с оригинальным посетителем, поэтому RTTI не всегда ошибается, но может привести к ужасному коду, если вы действительно не знаете, что делаете. ..

0

Если вы используете C++ 11 вы могли бы сделать что-то вроде:

int num_fish_sticks = std::count_if(buffer.begin(), buffet.end(), 
    [](const SeaFood* sf) {sf->is_fish_stick()}); 

вы должны были бы объявить чисто виртуальную функцию в морепродуктах:

virtual bool is_fish_stick() const = 0; 

и реализовать его в соответствие в подклассах.

EDIT: Конечно, это может быть грязно, если у вас слишком много подклассов. В этом случае, вы бы лучше всего с RTTI:

int num_fish_sticks = std::count_if(buffer.begin(), buffet.end(), 
    [](const SeaFood* sf) {typeid(*sf) == typeid(Fishstick)}); 
+0

Это еще хуже, чем при использовании RTTI. –

+0

@ AndreasMagnusson хорошо, OP хотел решения без использования RTTI. Я обновляю свой ответ, чтобы включить более общий способ, используя RTTI. – betabandido

+0

Yup, для хорошего решения см. Мои и сумасшедшие ответы Эдди. Шаблон посетителя - это способ «добавить» новые виртуальные функции в иерархию классов без изменения исходных классов. –

0

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

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

Если бы я собирался моделировать буфет, я, возможно, использовал бы контейнеры.Каждый лоток на вашем горячем столе - это контейнер, в котором есть что-то. Если вы хотите, вы можете смоделировать таблицу как контейнер для лотков и (возможно) комнату как контейнер таблиц.

Каждый лоток содержит «пищевой продукт» (или как вы предпочитаете его называть), для которого у вас в основном есть имя и количество. Единственным поведением может быть таймер, связанный с тем, когда еда была «подана», и на основании этого, когда все остальные необходимо удалить. У вас может есть немного больше, чем такие, как разделение продуктов на те, которые доступны только в рамках полной еды, а также на тех, кто только купил доступ к салатнику.

1

Предположительно у вас есть функция name в базовом классе, чтобы вернуть имя элемента, чтобы вы могли отобразить его на кухне. Просто используйте это, чтобы индексировать карту подсчета элементов.

В общем случае вы можете предоставить функцию, которая возвращает любой уникальный идентификатор для каждого класса.

1

Как насчет композитного рисунка? Буфет - действительно коллекция коллекций морепродуктов. FishStick и Squid являются «Компонентами» в композитном шаблоне, который будет поддерживать их количество элементов. Таким образом, когда Buffet является списком, он может перемещаться и рассчитывать счет на Composites.

Смежные вопросы