解释器模式中的错误处理
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 在您添加新代码时是否有效。
假设我想将阿拉伯数字 (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 在您添加新代码时是否有效。