假设我想要加上阿拉伯数字(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。我引用相关部分:
但是,引入一种语言及其附带的语法也需要对错误拼写的术语或错误的语法元素进行相当广泛的错误检查。
所以我的主要问题:如何最好地设计广泛的错误检查?
答案 0 :(得分:1)
你真的只想允许添加罗马数字或阿拉伯数字,但不是混合?如果混合没问题,那么你的代码就可以正常工作(假设你编写了实际的Roman-to-int解析代码)。如果您不想允许混合,则需要在对Plus()中的两个参数进行解释之前添加验证步骤。
例如,你可以dynamic_cast<ArabicNumber>
两个操作数,如果一个是NULL失败,如果两个都是NULL,请尝试dynamic_cast<RomanNumber>
。通常测试类的类型是代码味道,但在这里你要验证,所以没关系。
另一种方法是给每个Expression
一个CanBeConvertedToType()
方法,然后在该函数中有一个switch语句,它检查一个表示给定类型的常量,给定{{1}的结果类型。 1}}。这将是更加未来安全的,因为它允许您拥有所有返回相同类型的不同Expression
子类,而无需更改类型检查代码以检查添加时有效的每个类新的。