2010-04-01 3 views
4

나는 내 코드에서 정책을 많이 사용하고 있으며 대개 매우 만족 스럽습니다. 때때로 정책을 선택하고 상황을 해결하기 위해 habbits를 개발 한 상황에서 패턴을 사용하는 경우가 종종 있습니다. 보통 다음과 같이 시작합니다.나는 정책을 남용합니까?

class DrawArrays { 
protected: 
    void sendDraw() const; 
}; 

class DrawElements { 
public: 
    void setIndices(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); 
protected: 
    void sendDraw() const; 
}; 

template<class Policy> 
class Vertices : public Policy { 
    using Policy::sendDraw(); 
public: 
    void render() const; 
}; 

런타임에 정책을 선택하면 다른 상황에서 해결할 수 있습니다.

다른 코드 경로 :

if(drawElements) { 
    Vertices<DrawElements> vertices; 
} else { 
    Vertices<DrawArrays> vertices; 
} 

상속과 가상 통화 :

class PureVertices { 
public: 
    void render()=0; 
}; 

template<class Policy> 
class Vertices : public PureVertices, public Policy { 
    //.. 
}; 

두 솔루션 모두 나에게 잘못 생각합니다. 첫 번째는 유지 관리가 쉽지 않으며 두 번째는 가상 호출의 오버 헤드를 도입하여 처음부터 정책을 사용하여 피하려고했습니다.

적절한 해결책이 누락 되었습니까? 아니면 잘못된 패턴을 사용하여 문제를 해결합니까?

+0

런타임 선택 정책은 나에게 전략 패턴처럼 많이 보입니다. 나는 그것이 당신의 경우에 도움이되는지 확신하지 못합니다. – stefaanv

답변

0

처음에는 아무 것도 보이지 않습니다. 더 나은 리팩터링이 있는지를 판단 할만큼 충분한 코드가 없지만 유지 관리가 불가능한 것처럼 보이지 않습니다.

+1

상속에 대한 설명은 http://en.wikipedia.org/wiki/Policy-based_design 및 http://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315를 참조하십시오. – pmr

+0

참고해 주셔서 감사합니다. –

4

두 번째 버전을 사용하십시오. 가상 호출은 추가 포인터 조회가 필요하기 때문에 정적 호출보다 비용이 많이 들지만 "sendDraw"가 실제 도면을 수행하면 차이를 알 수 없습니다. 나중에 성능 문제가있는 경우 프로파일 러를 사용하여 문제점이있는 곳을 찾아 수정하십시오. 가상 메서드 호출이 실제로 성능 문제가되는 (극히 드문 경우) 정책을 사용하여 가상 메서드 호출을 최적화 할 수 있습니다. 그 때까지는 유지 관리가 가장 쉬운 코드를 작성하여 나중에 개발 시간을 최적화해야합니다.

Remeber : 조기 최적화는 모든 악의 뿌리입니다!

+0

나는 그것을 추가해야했다. 이 특별한 예제에서는 렌더링을위한 호출과 sendDraw()에 대한 많은 호출이있을 것입니다. 이것은 모든 예에서 다를 수 있습니다. 때때로 Policy 메소드에 대한 호출이 많거나 때로는 호스트 메소드에 대한 호출이 많이있을 수 있습니다. – pmr

+0

두 개 이상의 대안이있는 경우 vtbl 조회는 아마도 분기보다 빠를 것입니다. +1. –

+1

@ pmr : 전문가조차 정기적으로 성능 병목 현상을 잘못 판단합니다. (a) 많은 시간이 걸리고 (b) 대안이 빠를 것이라고 측정하지 않은 경우 코드를 최적화하지 마십시오. 최소한 빠른 코드 작성에 관심이있는 사람은 아닙니다. – Niki

0

일반적으로 런타임에 달라질 동작이 필요한 경우 switch/if 문 또는 가상 호출이든 관계없이 오버 헤드 비용을 지불해야합니다. 문제는 얼마나 많은 런타임 변화가 필요한가입니다. 적은 수의 유형 만 가질 것이라는 확신이 들면 switch 문이 실제로 적절할 수 있습니다. 가상 전화는 향후 확장에 더 많은 유연성을 제공하지만 반드시 유연성을 필요로하지는 않습니다. 그것은 문제에 달려 있습니다. 즉, '스위치 선언문'또는 '가상 전화'를 구현하는 방법은 여전히 ​​많이 있습니다. 스위치 대신/Visitor Pattern (유지 관리가 더 용이함)을 사용할 수 있고 가상 호출 대신 함수 포인터를 사용할 수 있습니다 (런타임에 호출되는 동작을 클래스 자체가 지정하는 것이 의미가없는 경우). 또한 나는 저자가 말한 모든 것에 동의하지 않지만 (나는 그가 인위적으로 자신의 생각과 OOP를 배타적으로 만든다고 생각한다.) 특히 여러분의 클래스 이름이 제안하는대로 렌더링을하고 있다면 Data Oriented Programming에 관심이있을 것이다.

+0

가상 호출은 컴파일러. 함수 포인터가 아닌 가상 호출이 여기에서 더 잘 될 것입니다. –

+0

@BillyONeal : 나는 부분적으로 동의하지 않습니다. 함수 포인터는 Functor로 쉽게 대체 할 수 있고 대부분의 경우 인라인 될 수 있도록 템플릿으로 처리됩니다. 가상 기능의 경우 반드시 필요한 것은 아닙니다. – pmr

+0

@Pmr : 왜 안 되니? 가상 함수 호출이 변경되지 않는 경우 (즉, 기본 클래스에 대한 참조가있는 경우) 함수 포인터가 변경되지 않으면 컴파일러는 가상 함수가 함수 포인터이기 때문에 두 경우 모두 동일한 최적화를 수행 할 수 있습니다. 유일한 차이점은 생성자에서 초기화하는 것을 잊지 마라는 것입니다. –

0

왜 가상 전화에 반대합니까? 오버 헤드가 정말로 중요한가요? 코드를 읽을 수없는 템플릿 대신 인터페이스와 다른 구현을 작성하여 수행하고자하는 것을 표현할 때 코드가 읽기 쉽다고 생각합니다.

어쨌든 VerticesPolicy 클래스에서 상속 받습니까? 이미 템플릿 인수로 사용하고 있습니다. 컴포지션이 여기에 더 적합 해 보입니다.상속을 사용하는 경우 하나의 템플릿이 아닌 클래스 Vertices 만 있고 다른 Policy 객체를 전달하여 동작을 변경할 수 있습니다. 이는 전략 패턴입니다.

+0

벤 콜린스 (Ben Collins)의 대답에 대한 설명은 상속에 대한 설명을 참조하십시오.OpenGL 호출에 익숙하다면 코드를 수정하여 이점을보다 분명하게 나타낼 것입니다. – pmr

0

그리기 호출을 표시 목록에 넣지 않으려면 그릴 때 배열 데이터를 복사해야합니다. (호출자가 GPU가 완료 될 때까지 차단되거나 드라이버가 앱 메모리에서 안전한 장소로 복사합니다.) 따라서 가상 함수는 문제가되지 않습니다. 디스플레이 목록에 넣으면 가상 기능이 문제가되지 않습니다. 단 한 번만 설정되기 때문입니다.

그리고 어떤 경우에도 PC는 가상 전화를 매우 빠르게 처리합니다. 그것들은 무료는 아니지만, 사실입니다. 그러나 프레임 당 수천 개의 정점 집합을 그릴 경우 드로잉 당 추가 가상 함수 호출은 은행을 깰 가능성이 거의 없습니다. 사전에 생각할 모든 것들 중에서, 가상 기능이 설계되는 바로 그 상황에서 가상 기능의 사용을 피하는 것은 아마 덜 중요한 것 중 하나 일 것입니다. 불필요하게 가상의 기능은 피해야합니다. 유죄를 입증 될 때까지 진정으로 유용한 가상 함수는

가 (호출 및 변경 쉐이더, 쉐이더 상수, 정점 포맷 당 정점을 그리기 및 자주 상태 설정을 렌더링 더 큰 배당금을 지불 할 가능성이있다.) ... 무죄

관련 문제