2010-07-14 4 views
1

나는 응용 프로그램 아키텍처에 다음과 같은 문제가 있으며 (많은 텍스트를 유감스럽게 생각합니다)을 해결하고자합니다.C++ 상속 질문

저는 게임 엔진 프로토 타입을 만들고 있는데 기본 추상 클래스가 있습니다 AbstractRenderer(저는 C++ 구문을 사용 하겠지만 여전히 문제는 일반적입니다).

이 렌더러의 일부 구현이 있다고 가정 해 보겠습니다. DirectxRendererOpenglRenderer을 예로 들어 보겠습니다. 이제

, 회원이이 시점에 모든 것을 분명히 을 촉구했다의이 렌더러의 하나 (의의 DirectX 기반에 충실하자) 가정 해 봅시다 것은 괜찮 - m_deviceDirectxRenderer에서 내부적으로 사용하고 노출되어서는 안된다 추상 AbstractRenderer 슈퍼 클래스.

일부 추상적 렌더링 인터페이스도 추가합니다 (예 : IRenderable). 그것은 단순히 하나의 순수 가상 메서드에게 virtual void Render(AbstractRenderer* renderer) const = 0;


을 의미한다 그리고 이것은 몇 가지 문제가 시작하는 장소입니다. 제가 어떤 장면을 모델링한다고 가정하면,이 장면은 아마 어떤 기하학적 물체를 가지고있을 것입니다.

추상 수퍼 클래스 AbstractGeometricalObject을 만들고 DirectX 기반 구현 DirectxGeometricalObject을 생성했습니다. 두 번째 포인터는 DirectX 관련 버텍스 & 인덱스 버퍼에 대한 포인터를 저장합니다.

지금 - 문제. 그것은 논리적 인 측면에서 렌더링 가능한을 때문에

AbstractGeometricalObject은 물론, IRenderable 인터페이스를 도출해야한다.

내가 내 DirectxGeometricalObjectAbstractGeometricalObject에서, 첫 번째가 있어야한다 virtual void Render(AbstractRenderer* renderer) const { ... } 방법을 도출하고, 그 Abstract... 물건 몇 가지 문제를 제공합니다.

더 나은 설명은 코드를 참조하십시오 :

그리고 지금 내 클래스는 다음과 같은 방법으로 모양을 위해 : 나는 아마 너무 많이 걱정하고있어 알고

class AbstractGeometricalObject : public IRenderable { 
    virtual void Render(AbstractRenderer* renderer) const { ... } 
}; 

class DirectxGeometricalObject : public AbstractGeometricalObject { 

    virtual void Render(AbstractRenderer* renderer) const { 

    // I think it's ok to assume that in 99/100 cases the renderer 
    // would be a valid DirectxRenderer object 

    // Assume that rendering a DirectxGeometricalObject requires 
    // the renderer to be a DirectxRenderer, but not an AbstractRenderer 
    // (it could utilize some DX-specific settings, class members, etc 

    // This means that I would have to ***downcast*** here and this seems really 
    // bad to me, because it means that this architecture sucks 

    renderer = dynamic_cast<DirectxRenderer*>(renderer); 

    // Use the DirectX capabilities, that's can't be taken out 
    // to the AbstractRenderer superclass 
    renderer.DirectxSpecificFoo(...); 
} 

,하지만 같은이 내리 뜬 간단한 사례는 응용 프로그램이 커지면 강제로 다운 캐스트를 많이 할 수 있음을 의미합니다. 분명히, 나는 이것을 피하고 싶습니다. 제발, 제게 조언을 좀 해주세요. 디자인상의 측면에서/제 실수를 지적 해주십시오.

답변

3

이 템플릿 패턴 (C++ 템플릿과 혼동하지 않도록) 편리 상황이 될 수도 있습니다 감사합니다.추상 클래스의 공개 Render은 가상이 아니고 개인 가상 기능 (예 : DoRender)을 호출해야합니다. 그런 다음 파생 클래스에서 대신 DoRender를 재정의합니다.

다음은 template pattern with private virtual functions의 사용법을 설명하는 기사입니다.

편집 :

내가 함께 내가 무엇을 의미하는지의 예를 넣어 시작하고, 실제로는 아키텍처의 광범위한 결함이있는 것처럼 보인다. AbstractRenderer를 사용하면 각 기하학적 객체가 특정 렌더러 유형을 밀접하게 인식하게되므로 다소 경솔합니다.

렌더러가 Renderable의 public 메서드를 처리 할 수 ​​있어야하거나 Renderable이 Renderer의 public 메서드를 처리 할 수 ​​있어야합니다. 또는 실제로 친밀한 연결이 필요한 경우 구체적인 렌더러에 Renderable 팩토리를 제공 할 수 있습니다. 잘 맞는 다른 패턴이있을 것이라고 확신합니다.

+0

문제는 내가 renderable (Render (AbstractRenderer * ...)와 Render (DirectxRenderer * ...)가 분리 된 렌더러를 가질 수 없기 때문에 여전히'Render' 메서드 인터페이스에만 국한되어있다. 행동 양식. 또는 나는 무엇인가 놓치 느냐? –

+1

@ HardCoder1986 : Render() 호출 방법에 대한 예를 들어 주시겠습니까? 윌 당신은 이것을 가지고 : ** [DirectXGeometricalObject * dxgo = new ...; dxgo.Render (directx_renderer)] ** 컴파일러가 어떤 유형이 Render로 전달되는지 정확하게 알고 있다면 원하는대로 할 수있는 방법을 찾은 것 같습니다. –

0

렌더러 var를 설정하고 한 곳에서 적절한 유형으로 캐스팅하려면 setter를 사용하십시오.

+0

설명해 주시겠습니까? –

+0

함수 서명을 변경하면 여전히 가상 재정의하지 않습니다. virtual void Render (DirectxRenderer * renderer)? – gtrak

+0

나는 * override *를 의미한다고 생각합니다. – Job

2

코드가 달성하고자하는 것이 표시되지 않습니다. Renderable 객체를 DirectXRenderables 및 OpenGLRenderables로 파생시킨 다음 Renderer에서 파생 된 무언가에 OpenGL 또는 DirectX 기능을 제공합니다. 특정 것은 말하기 위해서 또 다른 특정한 것을 사용합니다. 일반 렌더링 함수를 식별하고 pure virtual 추상 렌더러의 멤버로 만들고 DirectXRendererOpenGLRenderer에 구현하는 것이 훨씬 합리적입니다.

void draw(const AbstractRenderer& r) { 
    //general stuff 
    r.drawLine(...); 
    //only possible on directX 
    if(DirectxRenderer rx = dynamic_cast<DirectxRenderer*>(r)) { 
    //... 
    } else { 
    //throw exception or do fallback rendering in case we use something else 
    } 
} 
0

Bridge 디자인 패턴 당신을 도움이된다면보기 : ". 두 독립적으로 변화 할 수 있도록 구현에서 추상화를 분리"그럼 IRenderable은 거의 같은 멤버 함수 무승부있을 것입니다 귀하의 예제에서, AbstractGeometricalObject는 플랫폼 고유의 서브 클래스가있는 순수 가상 인터페이스 인 구현을 가리 킵니다. 까다로운 부분은 시간을내어 인터페이스를 발견합니다.

1

템플릿을 사용하면 IRendable을 두 클래스 (각 렌더러 유형에 하나씩)로 나눌 수 있습니다. 이것은 아마도 가장 좋은 대답은 아니지만 동적 캐스팅의 필요성을 피할 수 있습니다.

template <typename RendererType> 
struct IRenderable { 
    virtual void Render(RendererType* renderer) const = 0; 
} 

template <typename RendererType> 
class AbstractGeometricalObject : public IRenderable<RendererType> { 
    virtual void Render(RendererType* renderer) const { ... } 
}; 

class DirectxGeometricalObject : public AbstractGeometricalObject<DirectxRenderer> { 
    // this class will now require a void Render(DirectxRenderer* renderer) 
} 
0

컴파일러에서 거리를두고 이론을 고려해 봅시다. DirectxGeometricalObject::RenderAbstractRenderer이 아닌 매개 변수로 DirectxRenderer을 예상하는 경우 OtherGeometricalObject::Render은 아마도 OtherRenderer 개체를 매개 변수로 기대할 것입니다.

이렇게 서로 다른 구현 인 AbstractGeometricalObject은 서로 다른 서명을 가지고 있습니다. Render 서로 다르면 가상 AbstractGeometricalObject::Render을 정의 할 목적이 없습니다.

AbstractGeometricalObject::Render(AbstractRenderer*)을 선언하면 임의의 렌더러를 모든 기하학적 객체에 전달할 수 있어야합니다. 귀하의 경우에는 dynamic_cast이 실패 할 수 없으므로 그렇게 할 수 없습니다.