COM 인터페이스란?
C# 또는 Java를 알고 있는 경우 인터페이스는 친숙한 개념입니다. 인터페이스는 구현에 대해 아무 것도 지시하지 않고 개체가 지원할 수 있는 메서드 세트를 정의합니다. 인터페이스는 메서드를 호출하는 코드와 메서드를 구현하는 코드 간의 명확한 경계를 표시합니다. 컴퓨터 과학 용어에서 호출자는 구현과 분리됩니다.
C++에서 인터페이스와 가장 동등한 것은 순수 가상 클래스 즉, 순수 가상 메서드만 포함하고 다른 멤버는 포함하지 않는 클래스입니다. 인터페이스의 가상 예는 다음과 같습니다.
// The following is not actual COM.
// Pseudo-C++:
interface IDrawable
{
void Draw();
};
이 예제의 개념은 일부 그래픽 라이브러리의 개체 세트가 그리기 가능하다는 것입니다.
IDrawable
인터페이스는 그리기 가능한 개체가 지원해야 하는 작업을 정의합니다. (규칙에 따라 인터페이스 이름은 "I"로 시작합니다.) 이 예제에서 IDrawable
인터페이스는 단일 작업(Draw
)을 정의합니다.
모든 인터페이스는 추상이므로 프로그램에서 그렇게 IDrawable
개체의 인스턴스를 만들 수 없습니다. 예를 들어 다음 코드는 컴파일되지 않습니다.
IDrawable draw;
draw.Draw();
대신 그래픽 라이브러리가 IDrawable
인터페이스를 구현하는 개체를 제공합니다. 예를 들어 라이브러리는 도형을 그리기 위한 도형 개체와 이미지를 그리기 위한 비트맵 개체를 제공할 수 있습니다. C++에서 이 작업은 일반적인 추상 기본 클래스에서 상속하여 수행됩니다.
class Shape : public IDrawable
{
public:
virtual void Draw(); // Override Draw and provide implementation.
};
class Bitmap : public IDrawable
{
public:
virtual void Draw(); // Override Draw and provide implementation.
};
Shape
및 Bitmap
클래스는 그리기 가능한 개체의 두 가지 개별 형식을 정의합니다. 각 클래스는 IDrawable
에서 상속되며 Draw
메서드의 고유한 구현을 제공합니다. 당연히 두 구현은 상당히 다를 수 있습니다. 예를 들어 Shape::Draw
메서드는 선 세트를 래스터화할 수 있지만 Bitmap::Draw
는 픽셀 배열을 블릿할 수 있습니다.
이 그래픽 라이브러리를 사용하는 프로그램은 Shape
또는 Bitmap
포인터를 직접 사용하는 대신 IDrawable
포인터를 통해 Shape
및 Bitmap
개체를 조작합니다.
IDrawable *pDrawable = CreateTriangleShape();
if (pDrawable)
{
pDrawable->Draw();
}
다음은 IDrawable
포인터의 배열을 반복하는 예제입니다. 배열의 각 개체가 IDrawable
를 상속하면 배열에는 여러 종류로 이루어진 도형, 비트맵 및 기타 그래픽 개체 모음이 포함될 수 있습니다.
void DrawSomeShapes(IDrawable **drawableArray, size_t count)
{
for (size_t i = 0; i < count; i++)
{
drawableArray[i]->Draw();
}
}
COM의 핵심은 호출 코드에서 파생 클래스의 형식을 볼 수 없다는 것입니다. 즉, 코드에서 Shape
또는 Bitmap
형식의 변수를 선언할 수 없습니다. 도형 및 비트맵에 대한 모든 작업은 IDrawable
포인터를 사용하여 수행됩니다. 이러한 방식으로 COM은 인터페이스와 구현 간에 엄격한 분리를 유지 관리합니다.
Shape
및 Bitmap
클래스의 구현 세부 정보는 버그를 수정하거나 새 기능을 추가하는 등의 작업을 수행하기 위해 호출 코드를 변경하지 않고도 변경할 수 있습니다.
C++ 구현에서 인터페이스는 클래스 또는 구조를 사용하여 선언됩니다.
참고
이 항목의 코드 예제는 실제 연습이 아닌 일반적인 개념을 전달하기 위한 것입니다. 새 COM 인터페이스 정의는 이 시리즈의 범위를 벗어나지만, 헤더 파일에서 직접 인터페이스를 정의하지는 않습니다. 대신 COM 인터페이스는 IDL(인터페이스 정의 언어)이라는 언어를 사용하여 정의됩니다. IDL 파일은 C++ 헤더 파일을 생성하는 IDL 컴파일러에 의해 처리됩니다.
class IDrawable
{
public:
virtual void Draw() = 0;
};
COM을 사용할 때는 인터페이스가 개체가 아니라는 점을 기억해야 합니다. 개체가 구현해야 하는 메서드의 컬렉션입니다.
Shape
및 Bitmap
예제와 같이 여러 개체가 동일한 인터페이스를 구현할 수 있습니다. 또한 하나의 개체가 여러 인터페이스를 구현할 수도 있습니다. 예를 들어 그래픽 라이브러리는 그래픽 개체의 저장 및 로드를 지원하는 ISerializable
이라는 인터페이스를 정의할 수 있습니다. 이제 다음과 같은 클래스 선언에 대해 살펴보겠습니다.
// An interface for serialization.
class ISerializable
{
public:
virtual void Load(PCWSTR filename) = 0; // Load from file.
virtual void Save(PCWSTR filename) = 0; // Save to file.
};
// Declarations of drawable object types.
class Shape : public IDrawable
{
...
};
class Bitmap : public IDrawable, public ISerializable
{
...
};
이 예제에서 Bitmap
클래스가 ISerializable
을 구현합니다. 프로그램에서 이 메서드를 사용하여 비트맵을 저장하거나 로드할 수 있습니다. 그러나 Shape
클래스는 ISerializable
을 구현하지 않으므로 해당 기능을 표시하지 않습니다. 다음 다이어그램은 이 예제의 상속 관계를 보여 줍니다.
이 섹션에서는 인터페이스의 개념적 기초를 살펴보았지만 지금까지 실제 COM 코드를 보지는 못했습니다. COM 애플리케이션에서 수행해야 하는 첫 번째 작업인 COM 라이브러리 초기화부터 시작하겠습니다.