2008. 8. 25. 12:14

[Idioms] Pimpl Idiom

PImpl Idiom

- 인터페이스(Interface)와 구현(Implementation)을 구분해서, Coupling을 최소화
-
구현(Implementation)을 감출 수 있고, 구현의 세부내용이 변경되더라도, Client에서의 재컴파일을 피하게 할수 있다.

다른 이름

- Compilatioin Firewall
- Cheshire Cat
- Handle body

동기

- 클래스를 작성함에 있어서, 일반적으로 Interface정의하고, 클래스내에 실제 구현 클래스를 선언해서 Interface내부에 사용하게 된다. 이때 해당 구현 클래스의 내용이 변경이 되면, 이 클래스의 인터페이스만 참조만(Dll, Lib) 하는 Client들은 프로젝트 전체를 재 컴파일해야 되는 경우가 발생한다. 또한 클래스간의 의존성(dependency)가 늘어날수록, 작은 변경에도 컴파일 시간이 증가할수 있다.

example)

#Foo.h

#include “impl1.h”   // impl1의 변경되면, impl2.h, foo.h 역시 컴파일이 된다.

#include “impl2.h” // 이역시 마찬가지

Class foo

{

Public:

           Impl2 doSomeThing();

private:

            Impl _impl;

};

 

해결방법

클래스내에 두개의 구현 클래스로 추상화해서 분리한다. 첫번째로 클래스의 Interface와 구분자를 제공하고(이를 Handle이라고 한다), 두번째는 클래스의 실제 구현을 하게 된다(이를 Body라고 한다)

Example)

 

이렇게 Interface와 구현 분리함에 따라, 실제 구현의 내용이 변경되더라도, Interface를 참조하는 Client들의 변경 및 재컴파일이 필요없진다. DLL이나 Lib를 만들어 배포 할 때 좋은 방법이 될수 있다.

 

Comment

이 방법 역시 dependency를 최소화 한다는 의미에서 범용적으로 사용되는 용법이라고 볼수 있다. 그렇다고 무조건 적으로 이 방법이 효율적이라고 볼수 없는 것이 implementationInterfacePointer로 선언이 되어 있기 때문에, 구현부 어딘가에는 new/delete가 있어야 되므로, 이를 관리해야 된다는 부담이 없지 않아 있다. 또한 많이 사용되는 클래스라면, 성능상의 부담 또한 신경써야 된다. 때문에, 일반적으로 STL같은 표준라이브러리와 같이 거의 변경이 되지 않는 Object는 그냥 정의해서 사용하고, 구현 변경이 자주 되거나, 동적으로 변경될수도 있으며, 새로 개발하고 있는 구현들에서는 이 용법을 사용하는 것이 유리할것이라고 생각됩니다. 또한 pImpl을 추상화 시키면, Template Method, Factory Method등의 디자인 패턴에서 일반적으로 볼수 있는 형태가 된다.

 

알려진 사용 예

-       C++ 소프트웨어에서 아주 많이 사용된다.

 

관련된 Idioms

-       Counted Body(PImpl에서 reference counting개념을 넣었다) 위의 코드의 StringRep::count Counted Body에서 사용된다)

-       Smart Pointer(Pointee(라고 하나?)로 사용됨)

-       Envelope Letter(편지봉투라고 하나? -0- ) : PImle에서 구현를 추상화시켜서 사용하는 방식이라고 한다, 봉투(ADT(abstract data type)라고 한다)는 똑 같은데 내용(implementation)이 여러 개가 될수 있다는 건데, 쉽게 Template Method, Factory Method등을 생각하면 편할거 같다)

-       Handle Body Hierarchy : (먼지 모르겠다)

-       Checked delete : (delete의 안정성을 높이는 Idiom이라고 한다. Object type check를 통한 안전한 delete(좀 이상하다…sizeof(T)0이 될수도 있나? -_- ), 런타임이 아니고 컴파일 타임 체커(boost::checked_delete(), boost::check_array_delete()참조..)

-       Fast Pimpl : comment에서 언급한, new/delete의 문제를 해결 거, 동적 생성을 별도의 allocator(memory pool같은걸로)를 만들어 new/delete overloading 해서 사용한다.

 

More C++ Idioms 참고