2011-01-12 47 views
0

我建立了一個樹解析一些文本,所以我創造了一些節點:如何使用節點?

abstract class Node { } 

class TextNode : Node 
{ 
    public readonly string Text; 
    public TextNode(string text) 
    { 
     Text = text; 
    } 
    public TextNode(char ch) 
    { 
     Text = ch.ToString(); 
    } 
} 

class VariableNode : Node 
{ 
    public readonly string Name; 
    public VariableNode(string name) 
    { 
     Name = name; 
    } 
} 

我將很快添加一些「分支」節點(包含其他節點的節點)。

我想知道處理它們的最佳方法。現在我已經得到了一些代碼,看起來像這樣:

foreach (var item in nodes) 
{ 
    if (item is TextNode) 
     sb.Append(((TextNode)item).Text); 
    if (item is VariableNode) 
     sb.Append(dict[((VariableNode)item).Name]); 
} 

這似乎有點笨重,和我添加到它只會變得更糟。

我應該

  1. 嘗試封裝邏輯爲基礎Node類不知何故,這樣我就可以不用擔心的類型轉換的功能DoStuff()
  2. 我應該爲每種節點類型添加某種枚舉,以便可以使用開關而不是foreach循環嗎?
  3. 還有別的嗎?

需要給你們多一點背景。在上面的例子中,變量節點需要訪問字典來呈現自己。

我正在解析模板。在模板中,有變量。變量名和它們的值存儲在字典中。在解析時,我沒有這些值(嗯,我可以,但我希望能夠用不同的值重新呈現模板),所以我需要能夠將不同的字符傳遞給模板。我設置它的方式是,模板完成所有渲染,節點不做任何事情。我想我可以通過字典一個多級的每個節點,使他們能夠呈現自己...


爲了詳細說明#1

// in class Template 
public string Render(Dictionary<string, object> dict) 
{ 
    var sb = new StringBuilder(); 

    foreach (var item in nodes) 
     sb.Append(item.Render(dict)); 

    return sb.ToString(); 
} 

interface INode { 
    string Render(Dictionary<string, object> dict); 
} 

class TextNode : INode 
{ 
    private string _text; 

    public TextNode(string text) 
    { 
     _text = text; 
    } 
    public TextNode(char ch) 
    { 
     _text = ch.ToString(); 
    } 

    public string Render(Dictionary<string, object> dict) 
    { 
     return _text; 
    } 
} 

class VariableNode : INode 
{ 
    private string _name; 

    // TODO: filters... 

    public VariableNode(string name) 
    { 
     _name = name; 
    } 

    public string Render(Dictionary<string, object> dict) 
    { 
     return dict[_name].ToString(); 
    } 
} 

我想這是不是這樣的一個不好的解決方案。


解決方案迄今:

  1. 使用枚舉和開關
  2. 構建類型/操作的字典,使開關有點清潔
  3. 多態性(X2)
  4. Visitor pattern (還沒有讀過)
  5. 使用ANTLR - 雖然ANTLR會構建一棵樹,然後這問題再次適用

回答

2

您可以改爲使用interface並定義enum屬性,例如。 NodeType Type其中Type是具有值TextNode,VariableNodeenum。雖然,在這裏你也需要投入節點來獲得價值。您可以嘗試添加名爲Value的屬性,該屬性返回TextName,具體取決於NodeType。如果dict可從VariableNode訪問,則Value可返回您的方法所需的確切值。總而言之,我認爲接口比抽象類更適合您的需求。

+0

所以,#2但是有一個接口而不是抽象類。我意識到TextNode和VariableNode類目前看起來非常相似(並且可能會崩潰),但我主要區分它們是因爲它們需要以不同的方式進行處理。 「Dict」在VariableNode中不可用。事實上,直到......以後纔可用。解析完成後。 – mpen 2011-01-12 08:39:57

+0

+1;如果有基本的理由,只有不同的節點類別(通常不需要爲福特汽車和豐田汽車設置不同的類別 - 它們都只是一輛汽車)。同樣使用枚舉節點類型意味着你可以從一個醜陋的(和緩慢的)`if` /`else if`轉換爲擁有一個不錯的`switch`。 – slugster 2011-01-12 09:09:17

+0

您可以指定常用方法來區分所有工作的節點,就像我提到的屬性Value一樣,它會相應地返回Text或Name。節點越不同,越適合的接口變得越來越多,因爲處理節點的所有邏輯都將進入節點類。 – Yogesh 2011-01-12 09:11:41

2

如果你將有許多不同的節點類型與不同的行爲,我一定會考慮使用基類節點,然後爲每種類型實現特定的節點類,實現特定的行爲。

將樹處理邏輯放入節點的另一種方法是使用visitor pattern。在這種情況下,您會傾向於使用更加均勻的樹結構,並在樹之外維護特定的處理規則。

如果這棵樹是爲了解析文本的目的,你看看Antlr?該工具可以根據指定的語法爲您生成語法樹。

+0

我已經看過ANTLR ......沒有太多運氣。不太喜歡它。我將通過訪問者模式文章來看看,謝謝。 – mpen 2011-01-12 08:35:18

+0

這是真的ANTLR有一點學習曲線,當然,如果你想手工製作解析器,你總是可以創建更精簡的東西。但是,如果您可能想要更改或開發超時語法,那麼可以很好地消除解析代碼中的錯誤風險。祝你好運。 – 2011-01-12 11:03:21

1

多態性正是你想要的。事情是這樣的:

class Node 
{ 
    public virtual string ToDisplayText(params object[] parameters) 
    { 
     return string.Empty; 
    } 
} 
class TextNode : Node 
{ 
    public override string ToDisplayText(params object[] parameters) 
    { 
     return this.Text; 
    } 
} 
class VariableNode : Node 
{ 
    public override string ToDisplayText(params object[] parameters) 
    { 
     //check parameters 
     var dict = (Dictionary<string,string>)parameters[0]; 
     return dict[this.Name]; 
    } 
} 

因此,您可以:

foreach(var node in nodes) 
{ 
    sb.Append(node.ToDisplayText(dict)); 
} 
1

你可以創建一個Dictionary<Type,Action<T>> - 鍵是節點類型和Action<T> delegates你每做什麼。

你可以比簡單地做:

Dictionary[typeof(variable)]; 

爲了執行鍼對每種類型的正確的行動。

2

將您的foreach(節點中的var項)移動到抽象節點類中的方法(即BuildAll)中,創建一個抽象/虛擬方法Build(在抽象Node類中)並在BuildAll中調用Build()方法並讓其輸出一個字符串...

像這樣:

public abstract string Build(); 

    public string BuildAll() { 
     var output = new StringBuilder(); 
     foreach(var node in nodes) { 
     output.append(node.Build()); 
     } 
     return output.toString(); 
    } 

,並覆蓋它的每個實際實現節點的... 所以TextNode將裏面......

public override string Build() { 
    return ... extract whatever you want from TextNode; 
} 

在這種情況下,您的基類不需要知道其後代的確切實現,因此不會違反開放原則。