http://gamearchitect.net/2008/06/01/an-anatomy-of-despair-aggregation-over-inheritance/

김학규가 번역한 버전:

Despair 프로젝트의 초기 디자인을 하면서 나와 아드리안이 결정한 사항중 하나는 '가급적 상속보다 조합을 우선시하자'라는 점이었다. 이것은 내가 처음 생각해낸 것은 아니다. "조합 상속" 같은 단어로 구글을 검색해보면 수백만가지 결과를 찾아볼 수 있다. C++ 개발자 커뮤니티에서도 지난 몇년간 상속 개념의 용도가 불필요하게 많이 과장되었다고 회고하는 경향이 있다. Sutter 와 Alexandrescu 같은 저자들은 C++ 코딩 스탠다드라는 자신들의 책에 아예 '상속보다는 조합을 쓰라'는 가이드라인까지 정해놓았다.

그럼에도 불구하고 우리가 Despair 전에 작업했던 모든 게임 엔진들에는 비슷한 깊은 상속의 계층구조가 자리잡고 있었으며 90년대 중반에는 이런 구조가 유행이었다. '플레이어' 클래스는 '전투원' 클래스의 한 종류로 상속받았고, '전투원'은 다시 '이동체'에서, 그것은 다시 '물체' 에서, 그것은 다시 '베이스 개체' 에서 상속받는 식이다.

이런 구조에는 여러가지 약점들이 존재한다. 그중 몇가지를 열거해보자면:

첫째, 융통성이 부족하다. 만약 새로운 AI 를 가진 적을 만들면서 적 A 의 기능 몇가지와 적 B 의 기능 몇가지를 조합하고 싶다면 이런 문제는 상속구조로 간단히 표현하기가 어렵다. 이상적인 게임엔진이라면 디자이너가 프로그래머에게 의존하지 않고도 새로운 AI 들을 조합해낼 수 있어야 할 것이다. 하지만 깊게 구성된 상속구조에서는 항상 프로그래머가 관여하여 별로 좋지 않은 대안들 중에서 하나를 억지로 선택해야만 한다. 그 대안들의 예를 들자면, 새로운 클래스를 상속시키고 기존 코드들로부터 코드를 복사하던가, 아니면 다중상속구조를 쓰면서 '혹시 다이아몬드 형으로 꼬이진 않겠지'라고 바랄 수 밖에 없는 식이다.

둘째, 계층 구조에 있는 클래스들은 게임 개발이 지속되면서 그 복잡도가 통제할 수 없을 정도로 불어나게 된다. 만약 플레이어 클래스가 오브젝트 계층구조의 일부라면, 이 클래스에 입력과 조작 시스템 관련 부분, 커스텀 애니메이션 표현 부분, 아이템을 줍고 인벤토리를 관리하는 부분, 적을 타게팅을 보조하는 부분과 네트워크 정보를 동기화하는 부분, 그 외에 게임에 특화된 부분들을 만들어넣어야 한다. 우리가 전에 작업했던 한 게임에서는 플레이어 클래스의 구현이 13,000 라인이 넘어갔고, 플레이어 클래스는 12,000 가지의 물리적인 오브젝트 클래스로부터 상속받았다 (역자주 : 어떤 식으로 상속했길래 ...) 그런 파일들에선 무슨 내용을 찾기도 어렵고, 여러사람이 같이 작업하다보면 커밋하다가 충돌이 나는 부분이 많이 생겼다

세째, 깊게 구성된 상속 계층은 물리적(소스코드를 컴파일 하는 컴파일러의 입장)으로 볼때 별로 좋지 않은 구조이다. 클래스 A 가 B 로 부터 상속받고, B 는 C 로부터 상속받는다고 하면 A.h 는 B.h 와 C.h 를 포함해야만 한다. 상속 계층이 깊어지면 깊어질 수록, 더더욱 많은 직접 관련이 없는 헤더 파일을 포함해야만 하고, 컴파일 시간은 기하급수적으로 느려지기 마련이다. 코드간의 관계가 느슨해질 수록 컴파일 시간은 빨라질 수 있다 (존 라코스의 저서 Large scale C++ design 에서 이에 관련된 내용들을 참고하기 바람)

그래서 우리는 Despair 를 최대한 콤포넌트화하기로 했고, 상속구조도 가급적 평평하게 만들기로 했다. Despair 에 등장하는 오브젝트는 기본적으로 유니크ID 와 게임 콤포넌트를 담은 컨테이너를 래핑한 것에 불과하다. 컴포넌트는 그 타입 정보로 쿼리하고 동적으로 캐스팅해서 조작할 수 있다. 게임 오브젝트가 담당하는 부분은 오직 게임 오브젝트가 언제 소멸될 것인가에 대한 부분과 아이디를 관리하는 것뿐이다. 게임 오브젝트는 자기가 소유한 컴포넌트의 구현에 대해서는 알지 못하며, 일반적인 게임 오브젝트가 갖고 있는 위치, 충돌범위, 모양등에 대한 정보는 갖고 있지 않다.

이 방식으로 다른 시스템들도 구성하였다. 우리의 Scene 오브젝트의 구현도 게임 오브젝트와 유사하게 구현하여, 하나의 Scene 오브젝트가 각각의 화면에 등장할 Scene 노드들의 모음에 대한 활동주기를 관리하게 하였다. Scene 노드들은 각각의 계층구조를 통해 Render state 와 Skeletal transform 을 관리한다.

이런 시스템들은 또한 비쥬얼 에디팅을 위한 flow-graph 라이브러리 상으로도 구현되었다. 게임 로직, 애니메이션, 재질들을 프로그래머가 아닌 사람들도 그래프를 조작하는 것으로 연결할 수 있는 툴이 마련되었다.

우리에게 상속 대신 조합을 사용하는 방식은 매우 잘 작동한다. 이 시스템을 디자인하면서 가장 걱정한 사항중 하나는 혹시 실행속도가 떨어지는 것은 아닐까 하는 점이었다. 현재로서는 깊은 상속구조를 사용했던 예전보다 실행속도의 차이가 나타난 징후는 보이지 않는다. 설령 차이가 난다고 해도, 오히려 더 좋은 쪽으로 차이가 날 수도 있다고 추측한다. 왜냐하면 각 컴포넌트들이 잘 캡슐화되어 있기 때문에, 관련 코드들은 메모리상 가까이에 있게 되고, 캐쉬 locality 에 긍정적인 영향을 줄 수 있기 때문이다. 또한 각 컴포넌트중 변화가 생긴 부분에 대해서만 업데이트를 할 수 있기 때문에, 프레임 률에 따라 선별적인 업데이트를 하는 것으로 최적화할 여지도 있기 때문이다.

만약 다시 처음부터 시스템을 디자인할 기회가 생겨서 오브젝트 조합에 대한 부분을 바꾸게 된다면, scene 오브젝트를 game 오브젝트와 달리 좀 더 내부를 드러내지 않도록 할 생각이다. Scene 오브젝트에 주어진 문제는 다른 오브젝트와 차이가 있다. 현재 게임 오브젝트용 컴포넌트는 수백가지가 만들어졌으며, 가벼운 오브젝트 인터페이스를 통해 컴포넌트의 타입을 얻어와 조합하는 방식의 융통성은 상당한 잇점을 안겨주었다. 내 생각에는 게임 오브젝트 시스템은 게임 플레이를 위해 빨리 컴포넌트를 추가해서 조립하는 점에서는 이상 해결책에 도달했다고 본다. 하지만 Scene 오브젝트는 좀 경우가 다르다. Scene 라이브러리를 만든 이후로 Scene 노드 타입은 추가된 적이 없으며, 모든 Scene 노드들은 상위 레벨 코드보다는 Scene 라이브러리 내부에서 구현되고 있다. 한편으로는 Skeletal 계층구조를 재계산 하는 부분에 대해 다양한 최적화를 시도해보면서 상위 레벨 코드들은 그대로 유지할 수 있도록 인터페이스를 유지하는 것이 바람직할 것이다. 이런 점들로 하여금, 디미터의 법칙을 따르고, Scene 오브젝트의 구현을 감추는 것이 Game 오브젝트를 금방 콤포넌트를 조립해 만들고 테스트하는 환경에는 바람직하지 않겠지만 Scene 오브젝트에게는 더 적절하게 적용될 수 있게 만든다.

이론적으로 완벽한 소프트웨어의 세계에서와는 달리 사람이라는 요소가 결부된 실무의 세계에서는 콤포넌트 기반의 디자인을 적용하게 되면 몇가지 당황스러운 점들이 생겨난다. 신입 프로그래머들은 콤포넌트 기반의 엔진을 사용하기 위해 더 가파른 학습곡선을 넘어가야만 한다. 몇가지 중요 함수들을 거치다보면 전체 시스템의 큰 흐름을 이해할 수 있었던 예전 구조에 비해, 새로운 시스템에서는 계속 들어가도 들어가도 핵심이 금방 보이지 않아 혼동을 겪게 된다. 예를 들면 Despair 에는 이름에 '콤포넌트'라는 단어가 들어간 클래스가 500 개가 넘고 object 라는 이름이 들어간 클래스가 100 개가 넘는다. 우리의 게임은 C++ 오브젝트에 의해 정의되는 것이 아니라, 오브젝트간의 관계에 의해 정의 된다. 그 관계를 이해하기 위해서는 예전보다 훨씬 문서화와 커뮤니케이션의 비중이 커질 수 밖에 없다.

컴포넌트 기반으로 가면서 얻은 또 다른 교훈은, 컴포넌트 기반의 디자인에서는 코드에 있던 복잡도를 데이타로 옮겨놓게 되는 경향이 있다. 디자이너들은 오브젝트를 디자인하고 상속구조를 구성하는데 익숙해있지가 않다. 컴포넌트를 갖고 작업하게 되면 새로운 프로세스와 좋은 사람들을 필요로 하게 된다. 상호교류는 필수적이다. 프로그래머들은 컴포넌트용 코드를 작성하는 것 외에도, 툴에 넣을 오브젝트를 만들기 위한 작업을 해야 하고, 테크니컬 디자이너들과 공동으로 작업하면서 피드백을 받고, 디자이너들에게 더 많은 재료들을 제공해야만 한다. 게임과 같이 팀은 각각의 멤버에 의해 정의되는 것이 아니라, 멤버간의 관계에 의해 정의되는 것이다

imcgames 의 김학규입니다