【发布时间】:2014-12-12 09:48:18
【问题描述】:
我正在编写一个脚本解释器,我首先需要标记一个包含源代码的字符串。为此,我发现了不同的东西:
- 标识符(变量名);
- 符号(+、-等...包括“字母”运算符,例如“return”);
- 文字值(true、false、1、3.14、“foo”)。
为了表示这一点,我考虑了两种不同的方式:要么创建类层次结构:
class Token
{
public:
enum type_e { E_IDENTIFIER, E_SYMBOL, E_LITTERAL }
const type_e type;
};
class Identifier : public Token
{
public:
const string name
}
class Symbol : public Token
{
public:
const symbol_e symbol;
}
class Litteral : public Token
{
public:
const Value value;
}
我会以这种方式使用向下转换:
bla bla parseStatement( bla bla )
{
// ...
Token * curTok = tokens[ curPos ];
if( curTok->type == E_SYMBOL && dynamic_cast< Symbol * >( curTok )->symbol == E_PLUS )
{
// ...
}
// ...
}
但有人告诉我,向下铸造意味着我的设计可能有缺陷。而且也违反了多态性原则。
然后我想到了第二种方法,使用某种包含所有内容的变体类:
class Token
{
private:
type_e _type;
public:
type_e getType()
bool isIdentifier()
bool isSymbol()
bool isLitteral()
string getName() // If is an identifier, else exception
symbol_e getSymbol() // If is a symbol, else exception
Value getValue() // If is a litteral, else exception
}
我会用这种方式:
bla bla parseStatement( bla bla )
{
// ...
Token curTok = tokens[ curPos ];
if( curTok.isSymbol() && curTok.getSymbol() == E_PLUS )
{
// ...
}
// ...
}
但在我看来,它并不干净。基本上是一样的,只是写的短了一点。
有人建议我使用访问者设计模式,但我想不出一种方法来解决我的问题。 我想将语法分析逻辑保留在标记之外。该逻辑将在一个语法分析类中,该类将操纵这些标记。 我还需要令牌具有相同的类型或具有公共基类,以便我可以将它们存储在单个数组中。
你知道我该如何设计这个吗?谢谢你:)
【问题讨论】:
-
我更喜欢您的解决方案。为了便于阅读,您可以添加
is*和as*方法,以便您的 if 检查可以读取curTok.isSymbol() && curTok.asSymbol()->symbol == E_PLUS然后asSymbol将执行dynamic_cast<Symbol*> -
感谢您的回答!它确实会使代码更容易读/写,但它仍然依赖于向下转换,所以它只会转移问题。
-
向下转换通常被认为是不好的,因为您试图获得比您要求的更具体的代码,但是 - 您实际上是在创建一个 run time type information 系统。
-
我认为一个巨大的变体类更难维护和测试,但我想这与偏好有关。
-
不是每个设计都应该是面向对象的。
pair<type_e,string>就足够了。
标签: c++ design-patterns polymorphism variant downcast