2012-06-24 55 views
1

我正在设计一个C++中的DNS解析库。 DNS数据包具有一组标准字段,后跟资源记录列表,其中又有一组标准字段,后跟RData字段。 RData字段根据类型字段进行分析。现在,我为DNSRData指定一个层次结构来处理各种类型。代码看起来像这样:类层次设计,避免从基类到派生类的downcast

class DNSRData { 
    virtual void ToString() = 0; 
    virtual void Parse() = 0; 
} 

class DNSRData_A : public DNSRData { 
    void ToString(); 
    void Parse(); 
    uint32_t GetIP(); 
} 

class DNSRData_CNAME : public DNSRData { 
    void ToString(); 
    void Parse(); 
    const char* GetAlias(); 
} 

class DNSResourceRecord { 
    /* Standard Fields 
    ..... */ 
    int  type_; // Specifies the format for rdata_ 
    DNSRData *rdata_; 
} 

class DNSPacket { 
    /* Standard Fields 
    ....*/ 
    vector<DNSResourceRecord *> rr_list_; 
} 

现在这是我的问题,每个DNSRData记录可能有不同的字段。我不想为Base类中的所有字段添加访问器,因为它们存在于某些派生类中,而不是其他类中的字段。 IP地址仅存在于DNSRData_A中,不存在于其他任何IP地址中。

因此,当我想对DNSRData执行任何操作时,我查找类型并从DNSRData *向DNSRData_A *执行downcast。

DNSRData *rdata = packet->GetResourceRecord().front(); //not really necessary for this example 
if(resource_record.type == RR_CNAME) { 
    DNSRData_CNAME *cname = (DNSRData_CNAME*)rdata; 
} 

这可能会导致以后出现大量问题,而且随着我们添加更多类型,它很快会变成一个邪恶的混乱。如何解决这个问题的任何想法,而无需向Base类中添加所有访问器?

编辑

一些更多的背景下,这是一种高性能的DNS解析跟踪库的一部分。当我们在电线上看到数据包时,很多操作都完成了。那么,什么样的操作会弄乱设计,让我们说我们得到一个DNSPacket,现在我们解析它,我们想要决定如何根据类型进一步处理它。

if(type == RR_CNAME) { 
    DNSRData_CNAME *cname = dynamic_cast<DNSRData_CNAME*>(&rdata); 
    char *alias = cname->GetAlias(); 
}else if (type = RR_A) { 
    DNSRData_A *a = dynamic_cast<DNSRData_A*>(&rdata); 
    uint32_t ip = a->GetIP(); 
} 

正如您所看到的,从基本类型RData到更具体的RData类型都存在一个倒退。我想避免这种沮丧,并使用可能的设计模式来解决这个问题。

回答

4

如果我理解你正确地我觉得Visitor模式是你在找什么

Visitor pattern

class GetInfoVisitor 
    { 
    void visit(DNSRData_A* a) 
    { 
     uint32_t ip = a->GetIP(); 
    } 
    void visit(DNSRData_CNAME * cname) 
    {   
     char *alias = cname->GetAlias(); 
    } 
    } 

class DNSRData 
{ 

    void action(Visitor& visitor) 
    { 
     visitor.visit(this); 
    } 
} 

int main() 
{ 
... 
GetInfoVisitor getInfoVisitor; 
DNSRData *rdata = packet->GetResourceRecord().front(); 
rdata->action(getInfoVisitor); 
} 
+0

jojo钉了它,我很确定访客正是你要找的。避免使用'virtual void ToString()'和'Parse()'成员函数,用'ToStringVisitor'和'ParseVisitor'类替换它们。 – mergeconflict

+0

当某些派生类中存在某些方法而不是其他方法时,访问者将如何工作。例如:GetIP只有DNSRData_A和GetAlias只在DNSRData_CNAME中,都来自基本DNSRData – creatiwit

+0

@shrin我添加了一些代码,我希望它可以帮助 – jojo

0

首先,您可能需要考虑dynamic_cast,而不是您在示例中使用的c样式。这将允许您检查演员是否成功,这可以避免潜在的严重错误。

其次,我想更多的上下文可能需要回答你的问题。几乎总是,良好地使用设计模式可以让你避免这种情况。鉴于手头的信息,我建议甚至可能会创建一个抽象的虚函数,称为在父类中的操作,然后可以覆盖它来实现感兴趣的特殊逻辑。这可以让您在有人重写基类时考虑问题,因此代码更易于维护,并且可以节省时间,因为避免查找类型。

+0

我确实使用dynamic_cast,这是我勾画出的一个粗略的例子。我在5分钟内解析了1000万条记录,随着时间的推移dynamic_cast降低了性能。我在这个问题中增加了更多的上下文来解释问题所在。 – creatiwit