O que é uma interface COM?
Se você conhece C# ou Java, as interfaces devem ser um conceito familiar. Uma interface define um conjunto de métodos que um objeto pode dar suporte, sem ditar nada sobre a implementação. A interface marca um limite claro entre o código que chama um método e o código que implementa o método . Em termos de ciência da computação, o chamador é dissociado da implementação.
No C++, o equivalente mais próximo de uma interface é uma classe virtual pura, ou seja, uma classe que contém apenas métodos virtuais puros e nenhum outro membro. Aqui está um exemplo hipotético de uma interface:
// The following is not actual COM.
// Pseudo-C++:
interface IDrawable
{
void Draw();
};
A ideia deste exemplo é que um conjunto de objetos em algumas bibliotecas gráficas seja desenhado. A IDrawable
interface define as operações que qualquer objeto desenhável deve dar suporte. (Por convenção, os nomes de interface começam com "I".) Neste exemplo, a IDrawable
interface define uma única operação: Draw
.
Todas as interfaces são abstratas, portanto, um programa não pôde criar uma instância de um IDrawable
objeto como tal. Por exemplo, o código a seguir não seria compilado.
IDrawable draw;
draw.Draw();
Em vez disso, a biblioteca de gráficos fornece objetos que implementam a IDrawable
interface . Por exemplo, a biblioteca pode fornecer um objeto de forma para formas de desenho e um objeto bitmap para desenhar imagens. No C++, isso é feito herdando de uma classe base abstrata comum:
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.
};
As Shape
classes e Bitmap
definem dois tipos distintos de objeto desenhável. Cada classe herda de IDrawable
e fornece sua própria implementação do Draw
método . Naturalmente, as duas implementações podem ser consideravelmente diferentes. Por exemplo, o Shape::Draw
método pode rasterizar um conjunto de linhas, enquanto Bitmap::Draw
blit uma matriz de pixels.
Um programa que usa essa biblioteca gráfica manipularia Shape
objetos e Bitmap
por meio IDrawable
de ponteiros, em vez de usar Shape
ponteiros ou Bitmap
diretamente.
IDrawable *pDrawable = CreateTriangleShape();
if (pDrawable)
{
pDrawable->Draw();
}
Aqui está um exemplo que faz loop sobre uma matriz de IDrawable
ponteiros. A matriz pode conter uma variedade heterogênea de formas, bitmaps e outros objetos gráficos, desde que cada objeto na matriz herda IDrawable
.
void DrawSomeShapes(IDrawable **drawableArray, size_t count)
{
for (size_t i = 0; i < count; i++)
{
drawableArray[i]->Draw();
}
}
Um ponto importante sobre COM é que o código de chamada nunca vê o tipo da classe derivada. Em outras palavras, você nunca declararia uma variável do tipo Shape
ou Bitmap
em seu código. Todas as operações em formas e bitmaps são executadas usando IDrawable
ponteiros. Dessa forma, o COM mantém uma separação estrita entre interface e implementação. Os detalhes de implementação das Shape
classes e Bitmap
podem ser alterados, por exemplo, para corrigir bugs ou adicionar novos recursos, sem alterações no código de chamada.
Em uma implementação C++, as interfaces são declaradas usando uma classe ou estrutura.
Observação
Os exemplos de código neste tópico destinam-se a transmitir conceitos gerais, não prática do mundo real. A definição de novas interfaces COM está além do escopo desta série, mas você não definiria uma interface diretamente em um arquivo de cabeçalho. Em vez disso, uma interface COM é definida usando uma linguagem chamada IDL (Interface Definition Language). O arquivo IDL é processado por um compilador de IDL, que gera um arquivo de cabeçalho C++.
class IDrawable
{
public:
virtual void Draw() = 0;
};
Quando você trabalha com COM, é importante lembrar que as interfaces não são objetos. São coleções de métodos que os objetos devem implementar. Vários objetos podem implementar a mesma interface, conforme mostrado com os Shape
exemplos e Bitmap
. Além disso, um objeto pode implementar várias interfaces. Por exemplo, a biblioteca de gráficos pode definir uma interface chamada ISerializable
que dá suporte a salvar e carregar objetos gráficos. Agora considere as seguintes declarações de classe:
// 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
{
...
};
Neste exemplo, a Bitmap
classe implementa ISerializable
. O programa pode usar esse método para salvar ou carregar o bitmap. No entanto, a Shape
classe não implementa ISerializable
, portanto, ela não expõe essa funcionalidade. O diagrama a seguir mostra as relações de herança neste exemplo.
Esta seção examinou a base conceitual das interfaces, mas até agora não vimos o código COM real. Começaremos com a primeira coisa que qualquer aplicativo COM deve fazer: Inicializar a biblioteca COM.