앞서

2013-02-08 2 views
0

긴 소프트웨어 아키텍처 질문을 그래프로앞서

선명도 편집 : 나는 객체 그래프에 NodeA, NodeB 같은 유형으로 구성 객체 그래프를, ... 변환하려고 그 *My*NodeA, *My*NodeB ... 등으로 구성되며 그 반대의 경우도 마찬가지입니다. NodeX 유형의 등록 정보는 MyNodeX 유형의 유사한 등록 정보에 해당하지만 대부분의 경우 이 아니며은 사소한 할당입니다.

이 같은 두 개의 유사한 클래스 구조가있는 경우 :

// pure model, i.e. minimal information that is convenient for storage

abstract class Node 
{ 
    public int BaseProperty { get; set; } 
    public NodeCollection Children { get; private set; } // : Collection<Node> 
} 

class NodeA /* NodeB, NodeC ... */ : Node 
{ 
    public int DerivedAProperty { get; set; } 
} 

// objects that are convenient for being used by the application

abstract class MyNode 
{ 
    public int MyBaseProperty { get; set; } 
    public MyNodeCollection Children { get; private set; } // : Collection<MyNode> 
} 

class MyNodeA /* MyNodeB, MyNodeC ... */ : MyNode 
{ 
    public int MyDerivedAProperty { get; set; } 
} 

을, 나는의 객체 그래프로 변환해야 NodeX 중 하나에 입력 MyNodeX 유형 또는 모든에서 NodeX 클래스 중 하나를 변경하지 않고 그 반대로, , 나는 나 자신이 정기적으로이 패턴을 사용하여 발견했습니다

NodeX -> MyNodeX

// USAGE/external code 
Node node = ... 
MyNode myNode = MyNode.Load(node, ARGS); // static factory
abstract class MyNode 
{ 
    ... 
    // factory 
    public static MyNode Load(Node node, ARGS) 
    { 
     var type = node.GetType(); 
     MyNode myNode; 
     // no 'is' usage because NodeB could be derived from NodeC etc. 
     if (type == typeof(NodeA)) 
      myNode = new MyNodeA(ARGS); // arbitrary ctor 
     else if (...) 
      ... 
     myNode.Load(Node); 
     return myNode 
    } 
    public virtual void Load(Node node) 
    { 
     this.MyBaseProperty = node.BaseProperty; 
     foreach (var child in node.Children) 
      this.Children.Add(MyNode.Load(child, this.ARGS)); 
    } 
} 
class MyNodeA : MyNode 
{ 
    ... 
    public override void Load(Node node) 
    { 
     var m = (NodeA)node; // provoke InvalidCastException if coding error 
     base.Load(node); 
     this.MyDerivedAProperty = m.DerivedAProperty; 
    } 
} 

MyNodeX -> NodeX

// USAGE/external code 
MyNode myNode = ... 
Node node = myNode.Commit();
abstract class MyNode 
{ 
    ... 
    // 'kind of' factory 
    public abstract Node Commit(); 
    public virtual Commit(Node node) 
    { 
     node.BaseProperty = this.MyBaseProperty; 
     foreach (var child in this.Children) 
      node.Children.Add(child.Commit()); 
    } 
} 
class MyNodeA : MyNode 
{ 
    ... 
    public override Node Commit() 
    { 
     var m = new NodeA(); // "factory" method for each type 
     this.Commit(m); 
     return m; 
    } 
    public override void Commit(Node node) 
    { 
     var m = (NodeA)node; // provoke InvalidCastException if coding error 
     base.Commit(node); 
     m.DerivedAProperty = this.MyDerivedAProperty; 
    } 
} 

나는이 접근 방식을 여러 번 성공적으로 사용했으며, 클래스에 추가해야하는 메소드가 곧 바뀌고 외래 코드도 마찬가지이기 때문에 일반적으로 좋아합니다. 또한 base.Load(node)/base.Commit(node)을 호출하여 코드 중복을 방지합니다. 그러나, 나는 정말로 그/그 밖의 정적 인 Load 팩토리 메소드의 사닥다리를 좋아하지 않는다. > MYNODE (Load)의 경우, 그것은 MYNODE에 얼마나 유사 - -> 노드 (Commit)의 경우

나는 노드에 대한 각 유형에 공장 방법을 선호한다. 그러나 staticvirtual은 분명히 약간 문제가 있습니다. 나는 또한 지금해야만하는 두 개의 캐스트를하지 않는 것을 선호합니다.

어떻게 든 이런 일이 가능합니까?

+0

탐색을 허용하고 방문자 패턴을 사용하여 한 유형에서 다른 유형으로 변환 할 수있는 데이터 구조를 사용합니다 (죄송합니다. 정성을 다할 시간이 없습니다 - 음식). –

+0

NodeA에서 NodeB? – sircodesalot

+0

@sircodesalot 'NodeA','NodeB' ...와 같은 타입으로 구성된 객체 그래프를'* My * NodeA','* My * NodeB'와 같은 타입으로 구성된 객체 그래프로 변환하려고합니다. ., 그 반대. – dialer

답변

1

제 생각에는 문제를 점진적으로 해결하는 것이 좋습니다. 먼저 트리를 통과하고 길을 따라 각 노드를 변환 할 뭔가가 필요합니다

당신은 또한 MYNODE에서 노드로 변환 속성을 복사하는 간단한 문제가 아니라는 것을 언급, 아직 때문에
public static class NodeExtensions 
{ 
    public static MyNode ToMy(this Node node) 
    { 
     var result = node.Transform(); 
     result.Children = node.Children.Select(ToMy).ToList(); 
    } 

    public static Node FromMy(this MyNode node) 
    { 
     var result = node.Transform(); 
     result.Children = node.Children.Select(ToMy).ToList(); 
    } 

    public static MyNode Transform(this Node node) 
    { 
     // TODO code to transform any single node here 
    } 

    public static Node Transform(this MyNode node) 
    { 
     // TODO code to transform any single node here 
    } 
} 

이 표시가 것이다 그 일이 계속 많아지면, 제 생각에 이것이 AutoMapper의 일이라는 것입니다.

AutoMapper를 사용하면 매핑 할 속성과 매핑에 적용 할 특수 규칙을 설명하는 "변환 프로필"을 만들 수 있습니다. 또한, 제네릭 메소드와 비 제네릭 메소드를 모두 제공하므로 컴파일 타임에 유형을 모르는 경우에도 사용할 수 있습니다. 엔티티와 뷰 모델을 변환하는 데 일반적으로 사용되므로 여기서 다른 용도와 관련된 많은 질문과 대답을 찾을 수 있습니다.

정의 형지도는 기본적으로이 같은 통화의 숫자로 구성

Mapper.CreateMap<Node,MyNode>(); // no special rules for this map 

당신은 분할 속성 또는 수행하는 형식 변환과 같은 특별한 매핑을 만드는 방법에 대한 세부 사항에 대한 AutoMapper documentation을 consule해야합니다. 두 방향으로 매핑 할 수 있도록지도를 만들어야합니다.

var myTree = node.ToMy(); 
// and back 
node = myTree.FromMy(); 
: 모든 장소에있을 때

public static MyNode Transform(this Node node) 
    { 
     return Mapper.Map(node.GetType(), node.GetMatchingMyType(), node); 
    } 

    public static Type GetMatchingType(this Node node) 
    { 
     // you can use a dictionary lookup or some other logic if this doesn't work 
     var typeName = "My" + node.GetType().Name; 
     return typeof(MyNode).Assembly.GetTypes().Single(t => t.Name == typeName); 
    } 

, 당신은 서면으로 전체 트리를 변환 할 수 있습니다 : 당신이 당신의 모든 매핑을 정의하면 같은

는 변환 확장 방법은 간단 할 수있다

+0

처음에는 AutoMapper를 훨씬 더 일찍 사용 했어야하는 것 같습니다. 이 솔루션은 복잡한 속성도 처리 할 수있을만큼 유연해야하므로 시도해 볼 것입니다. – dialer

0

모든 것이 위에서 제시 한대로 일관되게 나타 납니까?

그렇다면 리플렉션을 사용하는 일반적인 변환 변환 함수 집합을 만들 수 있습니다.

<T> ConvertTo<TMy, T>(TMy object) 
{ 
    // create an object of type T 
    T newObj = new T(); 


    // iterate the members of T using reflection 
    foreach(member in T) 
    { 
    // find the equavalent My members in TMy 
    // transfer the data 
    } 
    return newObj; 
} 

나는이 좀 더 들여다 그리고 아마도 언젠가는 이번 주말에 작업 코드 생성 : 내가 생각하고 무엇을 여기

가 (이의 의식이 아니라 확인 컴파일 된 코드의 흐름입니다).

+0

불행하게도, 'MyT'의 멤버를 'T'또는 그 반대로 동등한 멤버로 변환하는 것은 많은 경우에있어 중요하지 않습니다. * 그러나 더 큰 문제는 메서드 호출에서 'T'를 명시 적으로 지정해야한다는 것입니다. 즉, foreach 루프에서 자식을 변환 할 때 비슷한 if-else-ladder를 사용하여'T'를 결정해야한다는 것을 의미합니다 (이를위한 해결책은 제외). – dialer

관련 문제