¿Qué es una interfaz COM?
Si conoce C# o Java, las interfaces deben ser un concepto familiar. Una interfaz define un conjunto de métodos que un objeto puede admitir, sin dictar nada sobre la implementación. La interfaz marca un límite claro entre el código que llama a un método y el código que implementa el método . En términos de informática, el autor de la llamada se desacopla de la implementación.
En C++, el equivalente más cercano a una interfaz es una clase virtual pura, es decir, una clase que solo contiene métodos virtuales puros y ningún otro miembro. Este es un ejemplo hipotético de una interfaz:
// The following is not actual COM.
// Pseudo-C++:
interface IDrawable
{
void Draw();
};
La idea de este ejemplo es que se puede dibujar un conjunto de objetos en alguna biblioteca de gráficos. La IDrawable
interfaz define las operaciones que cualquier objeto dibujable debe admitir. (Por convención, los nombres de interfaz comienzan por "I"). En este ejemplo, la IDrawable
interfaz define una sola operación: Draw
.
Todas las interfaces son abstractas, por lo que un programa no pudo crear una instancia de un IDrawable
objeto como tal. Por ejemplo, el código siguiente no se compilaría.
IDrawable draw;
draw.Draw();
En su lugar, la biblioteca de gráficos proporciona objetos que implementan la IDrawable
interfaz . Por ejemplo, la biblioteca podría proporcionar un objeto shape para dibujar formas y un objeto de mapa de bits para dibujar imágenes. En C++, esto se hace heredando de una clase base abstracta común:
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.
};
Las Shape
clases y Bitmap
definen dos tipos distintos de objeto dibujable. Cada clase hereda de IDrawable
y proporciona su propia implementación del Draw
método . Naturalmente, las dos implementaciones pueden diferir considerablemente. Por ejemplo, el Shape::Draw
método podría rasterizar un conjunto de líneas, mientras que Bitmap::Draw
podría dividir una matriz de píxeles.
Un programa que usa esta biblioteca de gráficos manipularía Shape
objetos y Bitmap
a través IDrawable
de punteros, en lugar de utilizar Shape
punteros o Bitmap
directamente.
IDrawable *pDrawable = CreateTriangleShape();
if (pDrawable)
{
pDrawable->Draw();
}
Este es un ejemplo que recorre en bucle una matriz de IDrawable
punteros. La matriz puede contener una variedad heterogénea de formas, mapas de bits y otros objetos gráficos, siempre y cuando cada objeto de la matriz herede IDrawable
.
void DrawSomeShapes(IDrawable **drawableArray, size_t count)
{
for (size_t i = 0; i < count; i++)
{
drawableArray[i]->Draw();
}
}
Un punto clave sobre COM es que el código que realiza la llamada nunca ve el tipo de la clase derivada. En otras palabras, nunca declararía una variable de tipo Shape
o Bitmap
en el código. Todas las operaciones de formas y mapas de bits se realizan mediante IDrawable
punteros. De este modo, COM mantiene una separación estricta entre la interfaz y la implementación. Los detalles de implementación de las Shape
clases y Bitmap
pueden cambiar (por ejemplo, para corregir errores o agregar nuevas funcionalidades) sin cambios en el código de llamada.
En una implementación de C++, las interfaces se declaran mediante una clase o estructura.
Nota
Los ejemplos de código de este tema están diseñados para transmitir conceptos generales, no prácticas reales. La definición de nuevas interfaces COM está fuera del ámbito de esta serie, pero no definiría una interfaz directamente en un archivo de encabezado. En su lugar, se define una interfaz COM mediante un lenguaje denominado Interface Definition Language (IDL). Un compilador IDL procesa el archivo IDL, que genera un archivo de encabezado de C++.
class IDrawable
{
public:
virtual void Draw() = 0;
};
Al trabajar con COM, es importante recordar que las interfaces no son objetos. Son colecciones de métodos que los objetos deben implementar. Varios objetos pueden implementar la misma interfaz, como se muestra con los Shape
ejemplos y Bitmap
. Además, un objeto puede implementar varias interfaces. Por ejemplo, la biblioteca de gráficos podría definir una interfaz denominada ISerializable
que admita guardar y cargar objetos gráficos. Ahora tenga en cuenta las siguientes declaraciones de clase:
// 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
{
...
};
En este ejemplo, la Bitmap
clase implementa ISerializable
. El programa podría usar este método para guardar o cargar el mapa de bits. Sin embargo, la Shape
clase no implementa ISerializable
, por lo que no expone esa funcionalidad. En el diagrama siguiente se muestran las relaciones de herencia en este ejemplo.
En esta sección se ha examinado la base conceptual de las interfaces, pero hasta ahora no hemos visto código COM real. Comenzaremos con lo primero que debe hacer cualquier aplicación COM: Inicializar la biblioteca COM.