2013-11-26 1 views
3

에 있습니다. C# .Net 4.0 복합 패턴에서 일반적인 리프를 갖고 싶습니다. 내가 찾은 대부분의 예제는 전체 복합 트리를 통해 전파되는 기본 노드에 일반을 가지고 있습니다. 나는 그것을 원하지 않는다.일반 패턴이있는 복합 패턴이

다음 해결책을 찾았습니다. 두 개의 구현을 가진 INode라는 인터페이스. 하나는 기본적으로 INodes 사전입니다. 그것은 중복 된 leafs를 원하지 않기 때문에 사전입니다. ValueNode라고하는 다른 구현은 정보를 보유합니다. 이렇게하면 형식이 다른 리프 노드가 허용됩니다. 아무도 노드를 제거 할 수 있도록 내가 아이들을했습니다

public interface INode 
{ 
    string Name { get; } 
} 

public class CategoryNode : INode 
{ 
    public CategoryNode(string name) 
    { 
     this.Name = name; 
     this.Children = new Dictionary<string, INode>(); 
    } 
    public string Name { get; private set; } 

    public List<string> Keys 
    { 
     get { return this.Children.Keys.ToList(); } 
    } 

    private Dictionary<string, INode> Children { get; set; } 

    public INode this[string key] 
    { 
     get { return this.Children[key]; } 
    } 

    public void Add(INode node) 
    { 
     this.Children.Add(node.Name, node); 
    } 
} 

public class ValueNode<T> : INode 
{ 
    public ValueNode(
     string name, 
     T defaultValue) 
    { 
     this.Name = name; 
     this.Value = this.Default = defaultValue; 
    } 

    public ValueNode(
     string name, 
     T defaultValue) 
    { 
     this.Name = name; 
     this.Value = this.Default = defaultValue; 
    } 

    public T Default { get; private set; } 

    public T Value { get; set; } 

    public string Name { get; private set; } 
} 

공지 개인 나열합니다. 나는이 솔루션에 만족하고 있습니다. 그러나 그것이 생성하는 사용법 구문은 약간 말투입니다. 예를 들면 :

((this.root["category"] as CategoryNode)["leaf"] as ValueNode<int>).Value = (node as ValueNode<int>).Value; 

나는 나 구문을 단순화하기 위해

this.root["category"]["leaf"] = node; 

같은 것을 누구 아이디어가 있습니까 상상했다 동안?

+0

사전 <문자열, 사전 <문자열, 문자열 >>에 대해 생각해 보셨습니까? – rhughes

+1

인덱서에서 '동적'을 반환 할 수 있습니다. 그러나 타입 안정성이 느슨합니다. 그러나 항상 정확한 유형으로 캐스팅해야하기 때문에 타입 세이프티가 필요하지 않습니다. –

+0

@ rhughes : 아니요. 나는 카테고리가 카테고리 아이들을 가질 수 있다고 명시 적으로 언급하지 않았다. 따라서 루트 [ "category1"] [ "category2"] [ "leaf"]에 액세스 할 수 있기를 원합니다.이 중첩 사전을 사용하면 도움이됩니까? – Goosebumps

답변

1

확장 방법을 INode 유형에 추가하는 방법은 어떻습니까?

public static class INodeExtensions 
{ 
    public static void SetValue<T>(this INode node, string key, T v) 
    { 
     if(v is INode) 
     { 
      // category node set value 
      if(node is CategoryNode) 
      { 
       // convert and set value 
      } 
      else 
      { 
       throw new Exception("No children found."); 
      } 
     } 
     else 
     { 
      // value node set value 
     } 
    } 
} 
+0

타입 검사를 통제하고 예외가 던져지기 때문에 나는 이것을 좋아한다. 코드를 훨씬 더 짧게 만듭니다. 나는 또한 가치를 얻기 위해 하나를 만들 수 있습니다. 나는 그 순간에 그것을 가지고 놀고있다. – Goosebumps

1

리프에 대한 "경로"를 지정하기 위해 매개 변수 배열을 사용하는 것은 어떻습니까? 선택적으로 범주 노드를 가져올 필요가있는 경우를위한 또 다른 방법이 있습니다.

class CategoryNode : INode 
{ 
    public CategoryNode GetCategoryNode(params string[] path) { 
     CategoryNode cat = (CategoryNode)this.Children[path[0]]; 
     for (int i = 1; i < path.Length; ++i) { 
      cat = (CategoryNode)cat.Children[path[i]]; 
     } 
     return cat; 
    } 
    public ValueNode<T> GetLeafNode<T>(params string[] path) { 
     INode first = this.Children[path[0]]; 
     if (path.Length == 1 && first is ValueNode<T>) return (ValueNode<T>)first; 

     CategoryNode cat = (CategoryNode)first; 
     for (int i = 1; i < path.Length - 1; ++i) { 
      cat = (CategoryNode)cat.Children[path[i]]; 
     } 
     return (ValueNode<T>)cat.Children[path[path.Length-1]]; 
    } 
} 

당신은 이런 식으로 사용하지 :

var leafNode = root.GetLeafNode<int>("cat1", "cat2", "leaf"); 
// or 
root.GetLeafNode<int>("cat1", "cat2", "leaf").Value = 1234; 

이 인덱서가 더 이상 필요.

0

나는 Teddy가 제안한 것과 결국 GetValue를 추가했습니다. 또한 인덱스 노드를 INode 인터페이스에 넣고 값 노드에서 호출 될 때 예외를 throw합니다. 이 방법으로 this.root["category"]["leaf"] 구문을 사용할 수도 있습니다. value 속성에 액세스하려면 여전히 ValueNode <>로 캐스팅해야합니다. 하지만 this.root["category1"]["category2"].SetValue<int>("leaf", 42) 할 수 있습니다.

관련 문제