解释器模式中的错误处理

Error Handling in Interpreter Pattern

假设我想将阿拉伯数字 (1+2) 或罗马数字 (I+II) 相加,并且我使用如下所示的解释器模式:

(代码源自此处:https://en.wikibooks.org/wiki/C%2B%2B_Programming/Code/Design_Patterns#Interpreter

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)?我能想到的唯一解决方案是以某种方式使用转换,但这听起来不像是一个优雅的解决方案。还是不应该那样使用模式?

当然,一种选择是为此编写两个单独的函数,但我很好奇它是否可以在一个函数中完成,因为我想将此模式用于更复杂的上下文无关语法。

编辑:我的问题也有描述here。我引用相关部分:

However, introducing a language and its accompanying grammar also requires fairly extensive error checking for misspelled terms or misplaced grammatical elements.

所以我的主要问题:如何最好地设计广泛的错误检查?

您真的只想允许添加罗马数字或阿拉伯数字,而不是混合使用吗?如果混合没问题,您的代码就可以正常工作(假设您为 Roman-to-int 编写了实际的解析代码,而您遗漏了那里)。如果您不想允许混合,则需要在对 Plus() 中的两个参数调用 interpret 之前添加一个验证步骤。

例如,您可以 dynamic_cast<ArabicNumber> 两个操作数,如果一个是 NULL,则失败,如果两个都是 NULL,请尝试 dynamic_cast<RomanNumber>。通常测试 class 的类型是一种代码味道,但在这里你正在验证,所以没关系。

另一种方法是给每个 Expression 一个 CanBeConvertedToType() 方法,然后在该函数中有一个 switch 语句,用于检查表示给定类型的常量与给定结果的类型Expression。这会更 future-safe 因为它允许你有不同的 Expression subclasses 所有 return 相同的类型,而不必改变 type-checking 代码来检查每个 class 在您添加新代码时是否有效。