我需要解析源代码.我已经确定了3种不同类型的标记:符号(运算符,关键字),litterals(整数,字符串等)和标识符.
我已经有了以下设计,其中有一个跟踪子类类型的基类,因此可以使用基类指针对其进行下载:
class Token
{
type_e type; // E_SYMBOL, E_LITTERAL, E_TOKEN
};
class Symbol : public Token
{
const symbol_e symbol;
};
class Litteral : public Token
{
const Value value;
};
class Identifier : public Token
{
const std::string name;
};
我需要将这些类存储在一个令牌数组中,这就是为什么我需要它们有一个共同的基类.然后我像这样使用它们:
if( cur->type == E_SYMBOL && static_cast< const Symbol * >( cur )->symbol == E_LPARENT )
{
// ...
}
我可以创建虚函数isSymbol,isLitteral,isIdentifer,每个子类都会覆盖,但我仍然需要将基类指针向下转换为子类指针,以便我可以访问子类的特定数据.
人们说向下转换意味着界面可能存在缺陷,并且它使语法非常沉重,所以我想找到另一种方式,但我不能.有些人建议访问者模式,但我担心这会无用地复杂化代码,我甚至不明白我如何使用访问者模式来解决这个问题.
有人可以帮忙吗?谢谢 :)
解决方法:
你有三个选择.每种解决方案都有其优点和缺点.
>将逻辑放入令牌类中,因此调用代码不需要知道它正在处理哪种令牌.
这将是“最纯粹的面向对象”解决方案.缺点是逻辑倾向于在基类和子类之间传播,这使得它更难以遵循.它也可能导致类增长相当大.但是编译器/解释器通常没有那么多的操作会成为问题.
>使用Visitor Pattern.
这是一个接口TokenVisitor,其中访问方法为令牌子类型重载,并在令牌上接受(TokenVisitor&)方法,每个子类将覆盖该方法以调用适当的访问重载.
您现在需要知道接口中的完整令牌类型集,但它允许保持类合理地小,并按行动分组逻辑通常更容易遵循.
>使用有区别的联盟,例如Boost.Variant.
这根本不是面向对象的.它将导致在整个地方切换类型,并可能看起来很难看.但由于逻辑总是在一起,因此通常更容易理解,特别是对于那些不理解代码背后的想法的人.