Это часто сложная проблема. Если вместо службы мы говорим, что имеем дело с животными - потому что мы можем использовать это как прокси-сервер для «вещей, которые могут что-то делать, а не других вещей». Скажем, у нас есть животные Fish
, Bird
, Cow
, Dog
и Cat
.
Итак, мы думаем о вещах животных в целом можно сделать:
- Move - по-разному, например,
Swim
, Fly
, Walk
- шуметь - называют это
Talk
- имеют свойства, такие как длина, количество ног, вес и т.д.
- Некоторые животные могут быть обучены «делать вещи» (собака может принести, попугай может «говорить» по требованию),
- Корова может производить молоко, но (для практических целей), ни одна из других.
Итак, мы имеем, например Fish
, который, очевидно, может swim
, но не может talk
, которую Dog
или Bird
может сделать. A Bird
может летать, но не может Swim
[да, есть отличные плавающие птицы, я упрощаю реальность].A Dog
может создавать звуки, но, безусловно, не может летать. A Cat
может издавать звуки, плавать (неохотно), но не сможет Fetch
.
Таким образом, становится проблемой того, как мы представляем эту способность делать определенные вещи, но не другие вещи.
Есть несколько решений, но в конечном счете, есть два возможных решения
- Фиктивные функции, которые ничего (например, пустая функция) не делают для тех функций, которые не применимы:
Talk
на Fish
или Swim
на Bird
.
- Условный код - например, функция
CanSwim
, которая отвечает за то, может ли животное плавать или нет. В этом случае функция Swim
может выдать исключение, если оно все еще вызывается.
В конечном счете, где-то мы должны знать, что они разные виды животных, а некоторые могут делать что-то, другие не могут. Либо просить плыть плавать не удастся (и не должно произойти), либо «ничего не сделает». Вы действительно должны определить, что правильно делать.
У меня есть эта проблема в моем проекте компилятора. У меня есть «абстрактное синтаксическое дерево», которое представляет исходный код в анализируемой форме. Таким образом, он имеет узлы для объявлений переменных, деклараций функций, назначений, двоичных операций, while-loops, for-loops и т. Д. И т. Д. Они имеют один и тот же базовый интерфейс, который обеспечивает функцию CodeGen
, которая фактически делает (промежуточное представление) - так двоичное выражение a + b
будет генерировать код для загрузки a
, загрузить b
, а затем добавить два вместе. Codegen для функции вызовет codegen для тела функции и т. Д.
Есть полдюжины других функций общего назначения, которые применяются к (почти) всем записям AST. Но есть несколько операций, которые действительно имеют смысл в конкретной ситуации, например, при работе с бинарными операторами, строки не поддерживаются промежуточным представлением как add
для конкатенации. Так что нельзя просто загружать, загружать и добавлять - нужно вызвать функцию strcat
. Для этих особых случаев я использую dynamic_cast
, чтобы проверить, соответствует ли тип в этом случае типу строки, и если да, то попадайте в кодовый путь специальной операции. Я мог бы добавить функцию isString
для ВСЕХ типов, но это было бы довольно неудобно и не очень полезно, так как МОСТ времени, это не важно. Я не думаю, что это отличное решение.
Кастинг - это знак того, что ваш дизайн выключен; вы не можете использовать полиморфизм в полной мере с проверками «if/else». Лучше разложить его на два ортогональных интерфейса. – duffymo
Вы знаете, что C++ поддерживает множественное наследование, поэтому вы можете наследовать класс из нескольких базовых (интерфейсных) классов. –
Вся идея этого OO заключается в том, что пользователь не знает и не заботится о том, какой из конкретных классов стоит за интерфейсом. –