Мне нужно разобрать исходный код. Я идентифицировал 3 различных типа токенов: символы (операторы, ключевые слова), литералы (целые числа, строки и т. Д.) И идентификаторы.
У меня уже есть следующий дизайн, с базовым классом, который отслеживает тип подкласса, так что его можно уменьшить с помощью указателя базового класса:
class Token
{
type_e type; // E_SYMBOL, E_LITTERAL, E_TOKEN
};
class Symbol : public Token
{
const symbol_e symbol;
};
class Litteral : public Token
{
const Value value;
};
class Identifier : public Token
{
const std::string name;
};
Мне нужно, чтобы эти классы хранились в одном массиве токенов, поэтому мне нужно, чтобы они имели общий базовый класс. Тогда я использую их так:
if( cur->type == E_SYMBOL && static_cast< const Symbol * >( cur )->symbol == E_LPARENT )
{
// ...
}
Я мог бы создать виртуальные функции isSymbol, isLitteral, isIdentifer, которые будут переопределять каждый подкласс, но мне все равно придется понизить указатель базового класса на указатель подкласса. так что я могу получить доступ к конкретным данным подкласса.
Люди говорят, что подавление означает, что интерфейс, вероятно, имеет недостатки, и это делает синтаксис очень тяжелым, поэтому я хотел бы найти другой путь, но я не могу. Некоторые люди предложили шаблон посетителя, но я боюсь, что это бесполезно усложнит код, и я даже не понимаю, как я могу использовать шаблон посетителя с этой проблемой.
Кто-нибудь может помочь? Спасибо 🙂
У вас есть три варианта. Каждое решение имеет свои преимущества и недостатки.
Поместите логику в классы токенов, чтобы вызывающему коду не нужно было знать, с каким токеном он имеет дело.
Это было бы «самым чистым объектно-ориентированным» решением. Недостатком является то, что логика имеет тенденцию распространяться между базовым классом и подклассами, что усложняет следование. Это также может привести к тому, что классы вырастут довольно большими. Но у компиляторов / интерпретаторов обычно не так много действий, чтобы это было проблемой.
Использовать Шаблон посетителя.
То есть есть интерфейс TokenVisitor
с visit
метод перегружен для подтипов токена и accept(TokenVisitor&)
метод на Token
который каждый подкласс переопределит, чтобы вызвать соответствующую перегрузку visit
,
Теперь вам нужно знать полный набор типов токенов в интерфейсе, но он позволяет сохранять классы достаточно небольшими и сгруппированными по действиям, за которыми обычно легче следовать логике.
Например, используйте дискриминационный союз Boost.Variant.
Это не объектно-ориентированный вообще. Это приведет к переключению типа повсюду и, вероятно, будет выглядеть ужасно. Но так как логика — все вместе, часто легче следовать, особенно для кого-то, кто не понимает идею позади кода.