2009-05-20 50 views
3

是否存在已知模式來繼承分層對象結構中的數據?我有一個分層的'Item'結構,它需要從它的'Parent'繼承它的'Type'(與默認值有相同的數據)。子項的類型可以自行修改,當父項的類型發生變化時,其類型不變的所有子項都應該獲得新的父類型。C中的數據繼承#

請注意,我不能假像

public string Type 
{ 
    get 
    { 
     if (type == null) 
      return Parent != null ? Parent.Type : null; 

     return type; 
    } 
} 

,因爲我必須填寫數據庫中的值,並且結構太深使用遞歸而不必擔心性能。

我現在能想起來的唯一辦法是

public string Type 
{ 
    set 
    { 
     type = value; 
     UpdateUnchangedChildren(value); 
    } 
} 

public int AddChild(Item item) 
{ 
    item.Type = Type; 
    return Items.Add(item); 
} 

有沒有更好的辦法? 謝謝。

回答

3

這是一個常見的問題,通常與維護各種分層設置/配置有關。所以,我想對它的解決方案可以被認爲是「一種模式」。

不管怎麼說,從內部結構的角度,你有兩個主要選擇:

  • 標準化結構
  • 非規範化的結構

「Normazlied」 是遞歸實現的一個。一段特定的數據總是存儲在一個地方,所有其他地方都有它的引用(例如,對於父節點)。結構很容易更新,但從中讀取可能是一個問題。

「非規範化」意味着每個節點都將存儲其級別的整套設置,並且無論何時更新節點,都需要一些時間才能沿層次結構展開並覈實所有子節點。但是閱讀操作是即時的。

因此,「非規範化」版本似乎被更廣泛地使用,因爲常見的設置方案是您很少更新它們,而經常閱讀它們,因此您需要更好的讀取性能。例如,Windows ACL security model使用「非規範化」方法快速進行安全檢查。你可以閱讀他們如何resolve conflicts between the "inherited" and explicit permissions (ACEs) by checking them in a specific order。這可能是你的特定系統的矯枉過正,但你可以簡單地有一個標誌,特定的值被覆蓋,或相反,重置爲「默認」...

更多細節取決於您的系統需求,你可能會有一個「混合」架構,其中一些領域將「正常化」,而其他領域則不會。但你似乎是在正確的方式。

1

我不是100%確定你在做什麼......但你可以使用泛型將父對象的類型傳遞給子對象......但是有一個setter沒有真的有道理......父對象的類型將在實例化時設置,所以爲什麼你需要一個setter來改變它。

假設你有這樣的事情......

public class Child<T> 
{ 
    public string Type 
    { 
     get { return typeof(T).ToString(); } 
    } 
} 

那麼,當你有一個Parent對象的任何類型的,你可以傳遞給你的孩子屬性...

public class ParentA 
{ 
    public Child<ParentA> ChildObj { get; set; } 
} 

public class ParentB 
{ 
    public Child<ParentB> ChildObj { get; set; } 
} 

public class ParentC 
{ 
    public Child<ParentC> ChildObj { get; set; } 
} 

調用任何這些ChildObj.Type屬性將分別返回ParentA,ParentB & ParentC。

Buit我有一種有趣的感覺,你還沒有完全解釋你想要做什麼。 你可以發佈一些更多的代碼示例顯示一個父類&子/項目類

+0

好的,我會盡力解釋它。 – reticent 2009-05-20 08:40:39

0

「...結構太深,不能使用遞歸而不擔心性能。」

您是否真的測過這個?你處理了多少物品,結構有多深,物品沒有自己的「類型」價值有多普遍?您的應用程序的性能目標是什麼?遞歸解決方案與這些目標相比如何?

對於人們來說,認爲遞歸很慢並且因此在沒有嘗試的情況下將其從考慮中排除時非常普遍。由於性能原因拒絕最簡單的設計而不先測量它是一個好主意。否則,你會發現一個更復雜的解決方案,當更簡單的解決方案工作得很好時。

當然,你的第二個解決方案也是使用遞歸,只是沿層次而不是向上。如果兒童插入物在不同的時間發生,並且可以吸收可能的性能命中,那麼也許這會更容易接受。

+0

關於遞歸的好處是,但是,我已經測試了它,我認爲懶加載和遞歸不會成爲一對。正如你所說的,加載該值比改變它更頻繁。 – reticent 2009-05-23 05:56:19

1

一個顯而易見的優化是緩存從父類獲得的值在讀取類型時。這意味着你最多隻能遍歷每條路徑一次(而天真的解決方案意味着你將一次又一次地遍歷每個包含它的路徑,這意味着最多O(h^2)而不是O(h)) 。如果你有更多的讀取而不是寫入,這將工作得很好。

考慮一下:

class Node 
{ 
    string _cachedParentType = null; 
    string _type; 
    string Type 
    { 
     get { return _type ?? _cachedParentType ?? (_cachedParentType = Parent.Type); } 
     set 
     { 
      _type = value; 
      foreach (var child in Children) { child._cachedParentType = null; } 
     } 
    } 
} 

這意味着有足夠的讀取和少寫,閱讀變成O(1)在最好的情況下,或者在最壞的情況,一個「緩存未命中」將花費O(1H) h是樹的高度;而更新是O(k),k是分支級別(因爲我們只更新一層!)。我認爲這通常會比UpdateUnchangedChildren解決方案(我假設遞歸更新節點一直到葉節點)更好,除非您的WAY讀取多於寫入。

+0

是否需要遞歸更新節點到葉子?這樣,如果孫子節點已經緩存了它的父類型(這又是祖父類型)並且祖父母的類型改變了,則孫子節點將返回無效值。 – reticent 2009-06-28 16:02:50