중복

2012-01-27 2 views
0

없이 객체의 아이의 방법을 노출 나는 그것의 상태 (예를 들어 EntityManager, InputManager, Physics을) 관리하는 데 사용하는 여러 서브 시스템 (자식 개체)가 클래스 Level의 객체를 가지고있다. Level의 외부 인터페이스에서 이러한 하위 시스템의 메소드 중 일부를 공개하려고합니다.중복

uint32_t Level::CreateEntity() 
{ 
    return entityManager.CreateEntity(); 
} 

Entity& Level::GetEntity(uint32_t entityId) 
{ 
    return entityManager.GetEntity(entityId); 
} 

uint16_t Level::CreateInputState() 
{ 
    return inputManager.CreateInputState(); 
} 

void Level::AttachInputState(uint32_t entityId, uint16_t inputStateId) 
{ 
    inputManager.AttachInputState(entityId, inputStateId); 
} 

InputState& Level::GetInputState(uint16_t inputStateId) 
{ 
    return inputManager.GetInputState(inputStateId); 
} 

이 솔루션은 Level 클래스 내부의 메소드 선언을 복제 및 서브 시스템에 대한 제어를 리디렉션 한 줄 호출을 작성하는 저를 필요

여기에 하나 개의 솔루션입니다. 과거에 작업 한 프로젝트에서 이것은 관리하기 까다로운 문제였습니다.

다른 해결책은 공용 인터페이스에서 하위 시스템을 노출하는 것입니다. Level 외부의 객체가 호출을 전달하는 것이 중요하지 않으므로이 문제를 피할 수 있습니다.

이 문제를보다 우아하게 처리 할 수있는 디자인이 있습니까?

+0

어린이 기능을 모두 표시하거나 일부 기능 만 표시하겠습니까? – Nobody

+0

왜 매니저 객체가'Level'의 멤버가되어야합니까? – wilhelmtell

+0

@Nobody 일부 특별한 것들. – Kai

답변

1

을 영업 I의 요청에 따라 의견에서 제안한 해결책을 게시 할 것입니다. 새로운 C++ 표준에 템플릿이있는 더 나은 템플릿이 있다는 것이 확실하지만 어쨌든 추악한 중고 프로세서 솔루션을 게시 할 것이므로 사용해야하지 않습니다!

#define FUNCTION_DECLARATION(RETURNTYPE, FUNCTIONNAME, ...) \ 
    RETURNTYPE FUNCTIONNAME(__VA_ARGS__) 

는 다음과 같이 클래스 선언에 사용되는 :

class Level { 
    FUNCTION_DECLARATION(uint32_t, CreateEntity); 
    FUNCTION_DECLARATION(Entity&, GetEntity, uint32_t); 
}; 

정의는 같을 것이다

:이 작업을 지금

#define FUNCTION_DEFINITION(RETURNTYPE, PROPERTY, FUNCTIONNAME, ...) \ 
    RETURNTYPE Level::FUNCTIONNAME(__VA_ARGS__) \ 
    { \ 
     return PROPERTY.FUNCTIONNAME(__VA_ARGS__); \ 
    } \ 

그리고 매우 추한 사용

FUNCTION_DEFINITION(Entity&, entityManager, GetEntity, uint32_t(entityId)) 

보증 할 수 없습니다. 이 코드는 대부분의 코드를 테스트하지 않은 모든 유형에서 작동합니다. 당신이 볼 수 있듯이 입력과 함께 "해킹"은 참조 나 포인터가 아닌 일반 유형에서만 작동합니다. 클래스에서 복사 생성자를 트리거합니다!

물론 이것은 VA_ARGS를 함수의 각 인수에 대한 변수 및 변수 이름으로 바꾸면 향상 될 수 있지만 다시 한번 말하지만 매우 지루한 일이며 각 인수 수에 대한 템플릿을 작성해야합니다. 사용하십시오. 결과적으로 이전과 거의 비슷한 코드가 생성됩니다.

다시 말씀 드리 자면, 이것들보다 훨씬 좋은 템플릿을 사용하십시오! 내가 그걸 어떻게하는지 모르겠다. 제발 나를 대신해 템플릿을 사용하는 방법을 알고있는 사람이 있다면 ** 코드 여기있는 모든 사람들이 아프게 느끼고있는 멋진 솔루션을 작성하십시오.

+0

아 감사합니다! 전체 매크로 코드를 입력하는 것은 아니지만 그럼에도 불구하고 유용합니다. – Kai

0

는 또 다른 객체가 EntityManager, Physics 등 LY private를 포함 나는 그 인터페이스가 더 우아한 만들 것 알고하는 것입니다 생각하고 public LY 원하는 각 함수를 호출 기능을 노출있는 클라이언트 기본 객체를 호출 할 수 있어야합니다. Level을 공용 인터페이스에서 해당 프록시 개체의 인스턴스로 만들고 선택적으로 해당 프록시를 복사 할 수 없거나 이동 불가능하게 만듭니다.

이렇게하면 작업량이 줄어들지 않습니다.), 인터페이스를보다 체계적이고 쉽게 사용할 수 있습니다.

예 :

class Physics { 
public: 
    T gravity() { ... } // we want them to be able to call this 

    T2 nope() { ... } // but not this 
}; 

class LevelPhysics { 
public: 
    T gravity() { return phys.gravity(); } 

private: 
    LevelPhysics(Physics& phys) : phys(phys) { } 

    Physics& phys; 

    friend class Level; 
}; 

class Level { 
public: 
    LevelPhysics GetPhysics() { return LevelPhysics(phys); } 

private: 
    Physics phys; 
}; 

그런 다음 당신은 불행하게도 "당신을 위해 이러한을 확인합니다"에는 언어 기능이 없다

const LevelPhysics& phys = lvl.GetPhysics(); 

phys.gravity(); 
// but you can't use Physics::nope 

처럼 사용할 수 있습니다. 당신은 스스로 코딩을 할 수 없습니다. 당신이 Physics의 비밀 멤버에 액세스하는 데 필요한 모든 클래스를 알고있는 경우

또는, 당신은 단순히이 작업을 수행 할 수 있습니다 다음

class Physics { 
public: 
    T gravity() { ... } 

private: 
    T2 nope() { ... } 

    friend class Level; 
}; 

class Level { 
public: 
    Physics physics; 
}; 

lvl.physics.gravity(); 
// but can't do lvl.physics.nope();, only Level can 
+0

내게 이것은 인터페이스와 코드 중복을 복잡하게 만드는 것처럼 보입니다. – Kai

+0

인터페이스가 어떻게 복잡합니까? 더 나은 조직과 교환하기 위해 한 줄 더 많은 코드를 요구하는 것은 나에게 그것을 단순화하는 것으로 보인다. 내 대답에 추가 할 무언가를하고 싶지 않다면이 작업을위한 코드 중복 문제는 없습니다. –

+0

@Kai 거기에 기존 코드가 있는지 확인하십시오. –

1

기능을 집계하는 또 다른 방법은 개인 상속을 통한 것입니다. 그런 다음 상속 된 전용 메서드 중 일부를 using 지시문을 사용하여 public 메서드로 바꿀 수 있습니다. 예 :

#include <iostream> 

class feature_A 
{ 
public: 
    void func_A1() { std::cout << "A1" << std::endl; } 
    void func_A2() { std::cout << "A2" << std::endl; } 
}; 

class feature_B 
{ 
public: 
    void func_B1() { std::cout << "B1" << std::endl; } 
    void func_B2() { std::cout << "B2" << std::endl; } 
}; 

class compound : private feature_A, private feature_B 
{ 
public: 
    // Provide these functions as-is. 
    using feature_A::func_A1; 
    using feature_B::func_B1; 

    // Combine these two functions. 
    void func_C2() { func_A2(); func_B2(); } 
}; 

int main() 
{ 
    compound c; 
    c.func_A1(); 
    c.func_B1(); 
    c.func_C2(); 
    // c.func_A2(); // error: ‘void feature_A::func_A2()’ is inaccessible 
} 

선언문의 한 가지 제한 사항은 이름이있는 것입니다. 동일한 기능의 오버로드가 여러 개인 경우 하나만 선택하여 공개 할 수는 없습니다. 템플릿 방법과 마찬가지로 : using을 사용하여 하나의 전문 분야 만 선택할 수는 없습니다.

+0

"기능"클래스 중 하나가 완전히 가상 기능을 사용하면 어려울 수 있다고 생각하지만이 솔루션이 마음에 듭니다. – Kai