2012-06-24 96 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