2014-12-04 1 views
0

저는 꽤 복잡한 그래프와 같은 데이터 구조를 가지고 있습니다. 명확성을 들어, 그것을 간단하게하자 : 당신이 볼 수 있듯이나는이 두 가지 비슷한 클래스를 가지고있다. 코드를 인수 분해하기 위해 어떤 설계를 사용합니까?

는, 우리의 그래프는 다른 그래프처럼되지 않습니다 :이 날 두 종류의 존재, 두 AbstractEdge에서 상속. 이 디자인은 변경할 수 없습니다. 지금, 나는이의 라인을 따라 두 개의 클래스를 설계 할 수 있다고 가정 : 실제로

class OrientedEdge1 
{ 
    Edge1 * edge; 
    bool orientation; 

    void foo() { edge->foo(); } 
    void bar1() { edge->bar1(); } 
} 

class OrientedEdge2 
{ 
    Edge2 * edge; 
    bool orientation; 

    void foo() { edge->foo(); } 
    void bar2() { edge->bar2(); } 
} 

을 OrientedEdge1 :: foo는()와 OrientedEdge2 :: foo는()는 더 이상 단지 하나의 메소드를 호출하는 것보다,하지만 아이디어는 그것들이 동일하고, AbstractEdge로부터 상속받은 메소드만을 호출한다는 것입니다.

코드를 코드화하기 위해 어떤 설계를 사용합니까? 나는 세 가지 방법을 생각하고있다 :

1. 무료 기능
foo_impl(AbstractEdge * edge) { edge->foo(); } 

class OrientedEdge1 
{ 
    Edge1 * edge; 
    bool orientation; 

    void foo() { foo_impl(edge); } 
    void bar1() { edge->bar1(); } 
} 

class OrientedEdge2 
{ 
    Edge2 * edge; 
    bool orientation; 

    void foo() { foo_impl(edge); } 
    void bar2() { edge->bar2(); } 
} 

프로

사용 :

  • 아주 간단한 솔루션, 모든 인수 분해하지 않는 것보다 훨씬 더합니다.

단점 :

  • 모든 방법을 실시 할 수 있습니다 무료 기능.

  • 선언 코드는 여전히 복제됩니다.

2. 상속을

class AbstractOrientedEdge 
{ 
    AbstractEdge * edge; 
    bool orientation; 

    void foo() { edge->foo(); } 
} 

class OrientedEdge1: public AbstractOrientedEdge 
{ 
    Egde1 * edge1() { return static_cast<Egde1*>(edge); } 

    void bar1() { edge1()->bar1(); } 
} 

class OrientedEdge2: public AbstractOrientedEdge 
{ 
    Egde2 * edge2() { return static_cast<Egde2*>(edge); } 

    void bar2() { edge2()->bar2(); } 
} 

프로를 사용 :

  • 더 인수 분해.

  • 나는이 두 클래스를 다형 적으로 사용할 계획이 없지만, 상속으로 인해 관련이 있다는 사실을 알고있는 사람은 나중에 유용하게 사용할 수 있습니다.

단점 :

  • 이 OrientedEdge1 :: 가장자리는 항상 *는 Egde1를 가리키는 것을 적용하는 생성자/세터에서 치료를해야합니다.

  • 어떻게 든 static_cast는 잘못되었습니다.

3.사용 템플릿
template <class EdgeT> 
class OrientedEdge 
{ 
    EdgeT * edge; 
    bool orientation; 

    void foo() { edge->foo(); } 
} 

class OrientedEdge1: public OrientedEdge<Edge1> 
{ 
    void bar1() { edge->bar1(); } 
} 

class OrientedEdge2: public OrientedEdge<Edge2> 
{ 
    void bar2() { edge->bar2(); } 
} 

프로

:

  • 대부분의 인수 분해.

  • 두 클래스 모두에 저장된 포인터의 캐스팅이 필요없는 올바른 유형을 가지고 있습니다.

단점 :

  • AbstractEdge.h을 포함하도록 강제 헤더에서 공유 코드의 실행을 계속해야합니다 (이전 방법과 앞으로 선언과 피할 수).

질문 : 어떤 방법을 사용 하시겠습니까? 다른 해결책이나 제안이 있습니까?

3

+1

빌드 효율을 고려하여 설계를 고려하지 마십시오. 정말로 문제가된다면 더 빨리 빌드 머신을 구입하십시오. 그러나 아마 조숙 한 빌드 최적화 일 것입니다. –

+0

@ Cheersandhth.-Alf "AbstractEdge.h"포함에 대한 제 관심은 빌드 시간이 아니라 순환 종속성에 관한 것입니다. 실제로 디자인 # 3을 사용하면 OrientedEdge (또는 OrientedEdge를 구현하는 데 필요한 클래스)가 OrientedEdge 데이터 멤버를 가지지 않아 포인터가 필요하지 않을 때 대신 포인터를 가질 수 있습니다. 이 상황은 내 구체적인 경우에 발생합니다. 실제로 저는 오래전에 # 3을 구현했지만, 이제는 리팩토링을하고 있습니다.이 클래스의 다른 아키텍처로 변경하는 것을 고려해보십시오. – Boris

+0

오, 어서 .... 잠깐만, 정말로? "이 질문에 대한 대답은 거의 전적으로 의견에 근거를 두는 경향이 있습니다."나보다 경험이 많은 사람의 의견을 듣는 것이 무엇이 잘못 되었나요? "사실, 참조 또는 특정 전문 지식이 아닌": 저는 소프트웨어 아키텍처가 특정 전문 지식을 필요로한다고 생각합니다. 경험을 토대로 한 특정 접근법을 사용하는 것이 다른 특정 사례보다 바람직하다는 의견이 있습니다. 알기. 또는 이미 답변에서 제안 된 대체 접근법에 대해 알아보십시오. – Boris

답변

1

1 3. 중복 보일러, 내가 원하는 목적지까지 한 움직임 구현을 제거합니다.

+0

"1과 3"은 무엇을 의미합니까? "1 또는 3"을 의미합니까, 아니면 실제로 둘을 결합하려고합니까? 흠 ... 생각하자. 후자의 아이디어는 흥미 롭습니다. 나는 "결합하는 것이 얼마나 도움이 될 것인가?"라고 말하려고했지만, 실제로 생각한 후에는 실제로 도움이되었습니다. "AbstractEdge.h", "Edge1.h"및 "Edge2.h"를 포함해야합니다 (dynamic_cast를 수행하지 않는 한 컴파일 타임에 상속 관계를 알고 자유 함수를 호출해야하므로). 자유 함수의 구현에 필요한 다른 헤더를 포함하는 것을 피할 수 있습니다. Thx, 나는 그것에 대해 생각하지 않았다! – Boris

+0

(참고 : 수락하기 전에 다른 흥미로운 대답이 있는지 기다릴 것입니다.) – Boris

+0

어쨌든 내 대답이 보류 중이므로 아무도 다른 대답을 추가 할 수 없습니다. 너의 충고를하기 위해 시간을내어 주신 Thx, 내가 할 수있는 선택이야 :-) 건배! – Boris