Обработка ошибок в шаблоне интерпретатора
Предположим, я хочу сложить либо арабские числа (1+2), либо римские числа (I+II), и я использую шаблон интерпретатора, который выглядит примерно так:
(код, полученный здесь: https://en.wikibooks.org/wiki/C%2B%2B_Programming/Code/Design_Patterns)
struct Expression {
virtual int interpret() = 0;
};
class ArabicNumber : public Expression {
private:
int number;
public:
ArabicNumber(int number) { this->number = number; }
int interpret(Map variables) { return number; }
}
class RomanNumber : public Expression {
private:
string number;
public:
RomanNumber(string number) { this->number = number; }
int interpret(Map variables) {
//somehow convert the roman number string to an int
}
}
class Plus : public Expression {
Expression* leftOperand;
Expression* rightOperand;
public:
Plus(Expression* left, Expression* right) {
leftOperand = left;
rightOperand = right;
}
~Plus(){
delete leftOperand;
delete rightOperand;
}
int interpret(Map variables) {
return leftOperand->interpret(variables) + rightOperand->interpret(variables);
}
};
Как я могу убедиться, что ошибочный запрос (1+II) обработан правильно? Единственное решение, которое я мог придумать, было как-то использовать кастинг, но это не похоже на элегантное решение. Или шаблон не должен использоваться таким образом?
Конечно, одним из вариантов было бы написать две отдельные функции для этого, но мне любопытно, можно ли это сделать в одной, потому что я хотел бы использовать этот шаблон для более сложной контекстно-свободной грамматики.
Изменить: моя проблема также описана здесь. Я цитирую соответствующий раздел:
Однако введение языка и сопровождающей его грамматики также требует довольно обширной проверки ошибок для неправильно написанных терминов или неуместных грамматических элементов.
Итак, мой главный вопрос: как лучше спроектировать такую обширную проверку ошибок?
1 ответ
Вы действительно хотите разрешить добавлять только римские или арабские цифры, но не смешивать? Если бы микс был в порядке, ваш код просто работал бы (предполагая, что вы пишете фактический код синтаксического анализа для Roman-to-int, который вы там оставили). Если вы не хотите разрешать смешивание, вам нужно добавить шаг проверки перед вызовом интерпретации обоих аргументов в Plus().
Например, вы могли бы dynamic_cast<ArabicNumber>
оба операнда, и если один из них равен NULL, то если оба имеют значение NULL, попробуйте dynamic_cast<RomanNumber>
вместо. Обычно тестирование типа класса - это запах кода, но здесь вы проверяете, так что все в порядке.
Другим подходом было бы дать каждому Expression
CanBeConvertedToType()
метод, затем есть оператор switch в этой функции, который проверяет константу, представляющую данный тип, типу результата данного Expression
, Это было бы более безопасным для будущего, так как позволяет вам иметь разные Expression
подклассы, которые возвращают один и тот же тип, без необходимости изменения кода проверки типов для проверки каждого класса, который действителен при добавлении нового класса.