저는 꽤 복잡한 그래프와 같은 데이터 구조를 가지고 있습니다. 명확성을 들어, 그것을 간단하게하자 : 당신이 볼 수 있듯이나는이 두 가지 비슷한 클래스를 가지고있다. 코드를 인수 분해하기 위해 어떤 설계를 사용합니까?
는, 우리의 그래프는 다른 그래프처럼되지 않습니다 :이 날 두 종류의 존재, 두 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(); }
}
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(); }
}
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
빌드 효율을 고려하여 설계를 고려하지 마십시오. 정말로 문제가된다면 더 빨리 빌드 머신을 구입하십시오. 그러나 아마 조숙 한 빌드 최적화 일 것입니다. –
@ Cheersandhth.-Alf "AbstractEdge.h"포함에 대한 제 관심은 빌드 시간이 아니라 순환 종속성에 관한 것입니다. 실제로 디자인 # 3을 사용하면 OrientedEdge (또는 OrientedEdge를 구현하는 데 필요한 클래스)가 OrientedEdge 데이터 멤버를 가지지 않아 포인터가 필요하지 않을 때 대신 포인터를 가질 수 있습니다. 이 상황은 내 구체적인 경우에 발생합니다. 실제로 저는 오래전에 # 3을 구현했지만, 이제는 리팩토링을하고 있습니다.이 클래스의 다른 아키텍처로 변경하는 것을 고려해보십시오. – Boris
오, 어서 .... 잠깐만, 정말로? "이 질문에 대한 대답은 거의 전적으로 의견에 근거를 두는 경향이 있습니다."나보다 경험이 많은 사람의 의견을 듣는 것이 무엇이 잘못 되었나요? "사실, 참조 또는 특정 전문 지식이 아닌": 저는 소프트웨어 아키텍처가 특정 전문 지식을 필요로한다고 생각합니다. 경험을 토대로 한 특정 접근법을 사용하는 것이 다른 특정 사례보다 바람직하다는 의견이 있습니다. 알기. 또는 이미 답변에서 제안 된 대체 접근법에 대해 알아보십시오. – Boris