我在编写解析器时遇到了问题。 以下函数都调用GetSymbol()函数。这些功能也可以互相调用。 Body()将调用Statement(),Statement()将调用Expression() 等等。
问题是,在任何函数中,符号列表都可以为空。我认为没有必要为每个函数添加额外的代码。相反,我在程序中添加了一个try catch。
以这种方式使用例外是不是错了 因为如果GetSymbol用完符号,则预期行为。
我应该避免抛出异常吗?
List<Symbol> symbols;
private void Term()
{...}
private void Expression()
{...}
private void Statement()
{...}
private void Body()
{...}
private Symbol GetSymbol()
{
if (symbols.Count > 0)
else
throw new OutOfTokenException();
}
public void Program()
{
try
{
while (Accept(Symbol.LBRACE))
Body();
}
catch (OutOfTokenException ote)
{
Output("Unexpected end of file");
}
}
}
答案 0 :(得分:1)
异常表示一组用例的异常大小写,这是您的程序并不真正期望的,但可能发生,这是一种常见的情况,尤其是在处理用户输出时。
我不理解的是 符号列表如何为空?如果符合您规则的有效文本主体可以为空,那么您将需要满足这些方案而无例外。
另一方面,如果一组空的符号表示用户应该已经完成但没有完成的某些符号,如果没有它,您的应用程序将无法运行,那么,您应该抛出异常。
答案 1 :(得分:1)
你是对的 - 抛出异常以处理好的情况是不好的做法。这与使用一般分支和控制流的异常几乎相同,这是一种反模式(here is a Java related question解释它)。
如果没有找到符号,您的GetSymbol()
方法应返回null - 除非您在没有符号时特别想要错误状态。
答案 2 :(得分:1)
尝试更改您的代码,这样您就有了一个点,您可以检查是否还有符号。像这样:
private Symbol GetSymbol()
{
// just your logic to get symbol
}
private bool SymbolExists()
{
return this.symbols.Count > 0;
}
并重写你调用代码:
public void Program()
{
while (SymbolExists())
{
Accept(GetSymbol(Symbol.LBRACE)
Body();
}
}
还要考虑将代码转换为独立的单元/类,如SymbolReader等。这样,您可以将“symbols”集合作为参数传递,并在分配之前对其进行验证。并且你可以保留你原来的GetSymbol()实现,以确保在有人滥用你的代码时(通过在阅读之前不调用SymbolExists())抛出适当的异常。
答案 3 :(得分:0)
您的代码缺少某些背景信息,例如类声明,它将告诉该类的公共接口是什么。我假设GetSymbol(Symbol.LBRACE)
调用是错误的,因为没有匹配签名的方法。
似乎类的前提条件允许它在任何时候调用Program
,因此Program
不应该抛出异常。请记住,每个方法都有前提条件和后置条件。当且仅当调用者违反前提条件时,应该抛出异常。
现在问题是:当没有符号可以获得时,是否可以拨打GetSymbol
?答案决定了它是否应该抛出异常。
但是:使用try / catch会模糊你的意图。这足以摆脱它的理由。像Yura所建议的SymbolExists
方法可以使你的意图完全清楚。
如果GetSymbol
为空,则错误的解决方案是null
返回symbols
并检查null
中的Accept
。你仍然摆脱了try / catch,但它同样模糊了你的意图,因此同样糟糕(再次恕我直言)。