Che cos'è un'interfaccia COM?
Se si conosce C# o Java, le interfacce devono essere un concetto familiare. Un'interfaccia definisce un set di metodi che un oggetto può supportare, senza dettatura dell'implementazione. L'interfaccia contrassegna un limite chiaro tra il codice che chiama un metodo e il codice che implementa il metodo . In termini di informatica, il chiamante è disaccoppiato dall'implementazione.
In C++, l'equivalente più vicino a un'interfaccia è una classe virtuale pura, ovvero una classe che contiene solo metodi virtuali puri e nessun altro membro. Ecco un esempio ipotetico di un'interfaccia:
// The following is not actual COM.
// Pseudo-C++:
interface IDrawable
{
void Draw();
};
L'idea di questo esempio è che un set di oggetti in alcune librerie grafiche è disegnabile. L'interfaccia IDrawable
definisce le operazioni che devono essere supportate da qualsiasi oggetto drawable. Per convenzione, i nomi di interfaccia iniziano con "I".) In questo esempio l'interfaccia IDrawable
definisce una singola operazione: Draw
.
Tutte le interfacce sono astratte, quindi un programma non può creare un'istanza di un IDrawable
oggetto come tale. Ad esempio, il codice seguente non viene compilato.
IDrawable draw;
draw.Draw();
La libreria grafica fornisce invece oggetti che implementano l'interfaccia IDrawable
. Ad esempio, la libreria potrebbe fornire un oggetto shape per disegnare forme e un oggetto bitmap per il disegno di immagini. In C++, questa operazione viene eseguita ereditando da una classe base astratta comune:
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.
};
Le Shape
classi e Bitmap
definiscono due tipi distinti di oggetto drawable. Ogni classe eredita da IDrawable
e fornisce la propria implementazione del Draw
metodo . Naturalmente, le due implementazioni potrebbero differire notevolmente. Ad esempio, il Shape::Draw
metodo potrebbe rasterizzare un set di righe, mentre Bitmap::Draw
avrebbe generato un'ampiezza di una matrice di pixel.
Un programma che usa questa libreria grafica modifica Shape
e oggetti tramite IDrawable
puntatori, anziché usare Shape
direttamente i puntatori o Bitmap
Bitmap
.
IDrawable *pDrawable = CreateTriangleShape();
if (pDrawable)
{
pDrawable->Draw();
}
Di seguito è riportato un esempio che esegue un ciclo su una matrice di IDrawable
puntatori. La matrice può contenere un assortimento eterogeneo di forme, bitmap e altri oggetti grafici, purché ogni oggetto nella matrice erediti IDrawable
.
void DrawSomeShapes(IDrawable **drawableArray, size_t count)
{
for (size_t i = 0; i < count; i++)
{
drawableArray[i]->Draw();
}
}
Un punto chiave su COM è che il codice chiamante non vede mai il tipo della classe derivata. In altre parole, non si dichiara mai una variabile di tipo Shape
o Bitmap
nel codice. Tutte le operazioni su forme e bitmap vengono eseguite usando IDrawable
puntatori. In questo modo, COM mantiene una stretta separazione tra l'interfaccia e l'implementazione. I dettagli di implementazione delle Shape
classi e Bitmap
possono cambiare, ad esempio per correggere i bug o aggiungere nuove funzionalità, senza modifiche al codice chiamante.
In un'implementazione C++ le interfacce vengono dichiarate usando una classe o una struttura.
Nota
Gli esempi di codice in questo argomento sono destinati a trasmettere concetti generali, non pratiche reali. La definizione di nuove interfacce COM non rientra nell'ambito di questa serie, ma non si definirà un'interfaccia direttamente in un file di intestazione. Al contrario, un'interfaccia COM viene definita usando un linguaggio denominato IDL (Interface Definition Language). Il file IDL viene elaborato da un compilatore IDL, che genera un file di intestazione C++.
class IDrawable
{
public:
virtual void Draw() = 0;
};
Quando si lavora con COM, è importante ricordare che le interfacce non sono oggetti. Sono raccolte di metodi che gli oggetti devono implementare. Diversi oggetti possono implementare la stessa interfaccia, come illustrato con gli Shape
esempi e Bitmap
. Inoltre, un oggetto può implementare diverse interfacce. Ad esempio, la libreria grafica potrebbe definire un'interfaccia denominata ISerializable
che supporta il salvataggio e il caricamento di oggetti grafici. Si considerino ora le dichiarazioni di classe seguenti:
// 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
{
...
};
In questo esempio la Bitmap
classe implementa ISerializable
. Il programma può usare questo metodo per salvare o caricare la bitmap. Tuttavia, la Shape
classe non implementa ISerializable
, quindi non espone tale funzionalità. Il diagramma seguente illustra le relazioni di ereditarietà in questo esempio.
Questa sezione ha esaminato la base concettuale delle interfacce, ma finora non è stato rilevato codice COM effettivo. Si inizierà con la prima operazione che deve essere eseguita da qualsiasi applicazione COM: Inizializzare la libreria COM.