[DirectX] COM(Component Object Model)

 COM은 DirectX의 프로그래밍 언어 독립성과 하위 호환성을 가능하게 하는 기술. COM 객체를 흔히 인터페이스라 부른다. C++클래스로 간주하고 사용. 그 세부사항은 프로그래머에게 드러나지 않는다.  프로그래머가 알아야 할 것은 COM 인터페이스로의 포인터를 특별한 함수들을 통해 얻거나, 다른 COM 인터페이스 메서드를 이용해서 얻는 방법 뿐이다.  COM인터페이스에는 new나 delete 키워드를 사용하지 않는다.  인터페이스를 다 사용하고 난 후에는 Release 메서드를 호출해줘야 한다. 각 COM 객체들이 자신만의 고유한 방식으로 메모리를 관리하기 때문.

디자인 패턴 - 객체 풀(Object pool)

객체를 매번 할당, 해제하지 않고 고정 크기 풀에 들어 있는 객체를 재사용함으로써 메모리 사용 성능을 개선한다. 메모리 단편화를 방지한다. 게임이 실행될 때 메모리를 미리 크게 잡아놓고 쓴다. 재사용 가능한 객체들을 모아놓은 객체 풀 클래스를 정의한다. 여기에 들어가는 객체는 현재 자신이 '사용 중'인지 여부 를 알 수 있는 방법을 제공해야 한다. 초기화될 때 사용할 객체들을 미리 생성하고(보통 같은 종류의 객체를 연속된 배열에 넣는다), 이들 객체를 '사용 안 함' 상태로 초기화한다. 새로운 객체가 필요하면 풀에 요청한다. 풀은 사용 가능한 객체를 찾아 '사용 중'으로 초기화한 뒤 반환한다. 객체를 더 이상 사용하지 않는다면 '사용 안 함' 상태로 되돌린다. 이런 식으로 메모리나 다른 자원 할당을 신경쓰지 않고 마음껏 객체를 생성, 삭제할 수 있다. 언제 쓰는가? 객체를 빈번하게 생성/삭제 객체들의 크기가 비슷 객체를 힙에 생성하기가 느리거나, 메모리 단편화가 우려됨 데이터베이스 연결, 네트워크 연결처럼 접근 비용이 비싸면서 재사용 가능한 자원을 객체가 캡슐화할 때 주의사항 메모리 낭비 가능성 사용 가능 객체 개수가 정해져 있다. 객체를 위한 메모리 크기가 고정되어 있다. (가장 큰 자료형에 맞춰야 함) 즉, 객체 크기 별로 풀을 나누는 게 좋다. 초기화에 주의 GC와의 충돌에 주의 사용 가능한 객체를 찾기 위해 빈칸 리스트(Free list) 를 고려해볼 수 있다. 객체가 풀과 커플링되는가?  풀을 통해서만 객체를 생성하고자 하면 그렇게 하라. 사용 중인지 여부를 객체 내부에서 알아낼 수 있다. 그렇지 않다면?  어떤 객체라도 풀에 넣을 수 있지만, 사용 중인지의 상태를 객체 외부에서 관리해야 한다. 객체 초기화 시 주의점은?  객체를 풀 안에서 초기화한다면, 객체를 완전히 캡슐화 가능하다. 풀 클래스가 객체 초기화 때문...

디자인 패턴 - 더티 플래그(Dirty flag)

계속해서 변경되는 ' 기본값 '이 있다. 또한 ' 파생값 '은 기본 값에 비싼 작업을 거쳐야 얻을 수 있다. 더티 플래그는 파생 값이 참조하는 기본 값의 변경 여부를 추적한다. 즉 더티 플래그는 기본값이 변경되면 켜진다 . 파생값을 써야 할 때, 더티 플래그가 켜져 있다면 다시 계산한 뒤에 플래그를 끈다 . 만약 플래그가 꺼져 있다면 이전에 캐시해놓은 파생 값을 그대로 사용 한다. 어떤 값이 더 이상 맞지 않다는 뜻으로 '더티'하다고 칭한다. 플래그란 둘 중 하나의 상태만 될 수 있는 작은 데이터를 뜻한다. 깨끗하지 않은 상태를 플래그로만 표시하고 그때그때 계산하지 않고 최종적으로 더티 플래그로 표시된 값들만 계산함으로써 중복된 계산을 피할 수 있다. 주로 계산과 동기화 작업에 사용할 수 있다. 사용 조건 파생 값이 사용되는 횟수보다 기본 값이 더 자주 변경되어야 한다. 점진적으로 업데이트하기가 어려워야 한다. 주의. 상당한 시간이 걸리는 작업을 지연해놨다가 결과를 보고 싶어 할 때 처리를 시작한다면, 짜증나는 랙이 생길 수 있다.

디자인 패턴 - 서비스 중개자 패턴(Service locator pattern)

서비스를 구현한 구체 클래스는 숨긴 채로 어디에서나 서비스에 접근할 수 있게 한다.  객체나 시스템 중에 거의 모든 코드에서 사용되는 것들이 있다. 이런 시스템은 게임 전체에서 사용 가능해야 하는, 일종의 서비스 라고 볼 수 있다.(메모리 할당, 로그, 난수 생성 등) 서비스를 사용하는 코드로부터 서비스가 누구인지 (서비스를 구현한 구체 클래스 자료형이 무엇인지), 어디에 있는지 (클래스 인스턴스를 어떻게 얻을지)를 몰라도 되게 해준다. 서비스는 여러 기능을 추상 인터페이스로 제공한다. 구체 서비스 제공자 (Service provider)는 이런 서비스 인터페이스를 상속받아 구현 한다.  이와 별도인 서비스 중개자 (Service locator)는 서비스 제공자의 실제 자료형과 이를 등록하는 과정은 숨긴 채 적절한 서비스 제공자를 찾아 서비스에 대한 접근을 제공 한다. 서비스 중개자는 서비스 제공자를 등록하고, 서비스를 사용하는 측에서 이 서비스를 이용할 수 있도록 중개해준다. 서비스 등록은?  외부 코드에서 등록할 수도 있고, 컴파일 시 바인딩될 수도 있다. 런타임에 설정 값을 읽을 수도 있다. 서비스를 못 찾으면?  사용자가 알아서 처리. 혹은 단언문으로 게임을 멈춤. 널 서비스 반환. 서비스 범위는?  전역(언제 어디에서 사용되는지 제어 불가)  특정 클래스로 제한(커플링 제어 가능. 중복 가능성) 사용처 :  접근해야 할 객체가 있다면 전역 메커니즘 대신  필요한 객체를 인수로 넘겨줄 수 없는지 부터 봐야 한다. 쉽고 커플링을 명확히 보여줄 수 있다.  렌더링 함수 매개변수에 렌더링에 관련된 것만 있어야지 로그 같은 게 섞여 있으면 곤란하다.  어떤 시스템은 본질적으로 하나뿐이다. 대부분의 게임 플랫폼에 오디오나 디스플레이 시스템이 딱 하나만 있다.  서비스 중개자는 일종의 더 유연하고 더 설정하기 좋은 싱...

디자인 패턴 - 타입 객체 패턴(Type object pattern)

의도 :  클래스 하나를 인스턴스별로 다른 객체형으로 표현할 수 있게 만들어, 새로운 '클래스들'을 유연하게 만들 수 있게 한다.  데이터는 게임 코드를 빌드하지 않고도 변경할 수 있어야 한다. 더 나아가 새로운 데이터를 만들고 수정하는 데에 프로그래머의 도움이 필요 없도록 한다.  예를 들어 수백 종의 몬스터를 전형적인 OOP 개념을 만들어내면, 몬스터의 하위 클래스가 너무나도 많아진다.  몬스터를 상속받는 하위 몬스터들을 계속 만들어내지 말고, 몬스터 공통의 동작인 몬스터 클래스와 종족을 나타내는 클래스를 따로 만들어서 구성하자.  이렇게 되면 종족 클래스는 몬스터의 '타입'을 정의한다. 각각의 종족 객체는 개념적으로 다른 '타입'을 의미한다.  그리고 설정 파일에서 읽은 데이터로 종족 인스턴스를 만들어낸다. 그러면 코드 상에서 클래스 상속으로 만들던 타입 시스템의 일부를 런타임에 정의할 수 있는 데이터로 옮긴 셈이다.  타입 객체(Type object)와 타입 사용 객체(Typed object) 클래스를 정의한다.  모든 Type object는 논리적으로 다른 타입을 의미 한다.   Typed object는 자신의 타입을 나타내는 Type object를 참조 한다.  타입 사용 객체는 타입 객체를 생성자에서 레퍼런스로 받고, 이 레퍼런스에서 초기값을 받거나 동작을 포워딩한다. 사용 : 나중에 어떤 타입이 필요할지 알 수 없거나(새로운 몬스터가 추가되는 등), 컴파일이나 코드 변경 없이 새로운 타입을 추가하거나 변경하고 싶을 때 사용한다. 주의 :  타입 객체를 직접 관리해야 함.  타입 별로 동작을 표현하기 어려움. 타입 종속적인 데이터 를 정의하기는 쉬우나, 타입 종속적인 동작 을 정의하기가 어렵다. 이를 위해서, 동작을 각각 구현한 함수를 정의하고 타입 객체가 적당한 함수 포인터를 저장하게끔 할 수 있다(마치...

디자인 패턴 - 바이트코드 패턴(Bytecode pattern)

게임에서의 마법 하나하나를 만들거나 고칠 때마다 코드를 수정하고 컴파일과 빌드를 거치기에는 반복이 너무 비효율적이다. 이럴 때, 코드를 수정하는 대신 코드는 특정 데이터를 읽게 하고, 데이터를 만들어서 반복을 수행하면 훨씬 효율적일 것이다. 행동을 데이터 파일에 따로 정의해놓고 게임 코드에서 읽어서 실행할 수 있다면 많은 장점이 있다. 데이터를 정의하기 위해 어떻게 할 것인가? 인터프리터 패턴 을 통해 데이터를 만들 수도 있겠다. 그러나, 인터프리터 패턴은 너무나도 느리고 메모리를 많이 소비한다. 더 빠른 수단을 찾아보자. 극단적으로 생각해보면, 기계어가 가장 빠를 것이다. 기계어의 장점. 밀도가 높으며, 선형적이고, 저수준이며, 그러므로 빠르다. 그러나 데이터를 기계어로 짜게하는 것은 어불성설이다. 대신 가상 기계어를 정의하면 어떨까? 그리고 그 가상 기계어를 실행하는 간단한 에뮬레이터도 만든다면? 이 가상 기계어는 게임에서 완전히 제어할 수 있으며, 밀도가 높고 선형적이고 상대적으로 저수준이다. 이 에뮬레이터를 가상 머신 이라 하고, 가상 기계어를 바이트 코드 라 하자. 바이트코드 패턴을 사용하면 좋은 경우 언어가 너무 저수준이라 만드는 데 손이 많이 가거나 오류가 생기기 쉽거나 컴파일 시간이나 다른 빌드 환경 때문에 반복 개발하기가 너무 오래 걸리거나 정의하려는 행동을 나머지 코드로부터 격리하고자 할 때 네이티브 코드보다는 느리므로 성능이 민감한 곳에 사용하지는 말자. 만드려는게 일종의 API라고 생각하면 좀 더 접근하기 쉬울 것이다. 마법 데이터를 만드는 예제를 통해 알아보겠다. 마법은 명령어 집합으로 생각한다. 그리고 그 명령어는 열거형 값을 배열에 저장해 데이터로 인코딩한다. 한 바이트로 전체 열거형 값을 다 표현할 수 있다. 데이터를 구성하는 코드가 실제로는 이런 바이트들의 목록이다보니 바이트코드라 부른다. 명령 하나를 실행하려면 어떤 명령인지 보고 이에 맞는 API메서드...

디자인 패턴 - 인터프리터 패턴(Interpreter pattern)

문자열을 읽어서 이를 문법 구조로 표현하는 객체 집합인 추상 구문 트리로 만든다. 이런 추상 구문 트리를 만드는 데에서 끝나지 않고 이를 실행한다. 표현식 혹은 하위표현식 객체로 트리를 만들고, 진짜 객체지향 방식으로 표현식이 자신을 평가하게 한다. 문법규칙을 클래스로 표현한 구조 도메인 특화 언어를 사용하기 위한 디자인 패턴 특화 언어는 범용 언어보다 고속으로 처리될 수 있다. 보통 각각의 심볼에 대한 클래스를 가진다. 특화 언어의 예: SQL, 통신프로토콜 언어 등 단점  코드를 로딩하면서 작은 객체를 많이 만들고 연결 객체와 객체를 잇는 포인터가 많은 메모리를 소모 포인터를 따라 하위표현식에 접근해야 하기 때문에 데이터 캐시에 치명적. 동시에 가상 메서드를 호출하는 것은 명령어 캐시에 치명적. 결론적으로, 느리다.