Qu’est-ce que la classe de proxy en C ++?

Qu’est-ce qu’une classe proxy en C ++? Pourquoi est-il créé et où il est utile?

Un proxy est une classe qui fournit une interface modifiée à une autre classe. Voici un exemple – supposons que nous ayons une classe de tableau que nous voulons seulement pouvoir contenir les chiffres binarys 1 ou 0. Voici un premier essai:

struct array1 { int mArray[10]; int & operator[](int i) { /// what to put here } }; ` 

Nous voulons que l’opérateur [] se plaint si nous disons quelque chose comme un [1] = 42, mais ce n’est pas possible car il ne voit que l’index de dans le tableau, pas la valeur stockée.

Nous pouvons résoudre cela en utilisant un proxy:

 #include  using namespace std; struct aproxy { aproxy(int& r) : mPtr(&r) {} void operator = (int n) { if (n > 1) { throw "not binary digit"; } *mPtr = n; } int * mPtr; }; struct array { int mArray[10]; aproxy operator[](int i) { return aproxy(mArray[i]); } }; int main() { try { array a; a[0] = 1; // ok a[0] = 42; // throws exception } catch (const char * e) { cout << e << endl; } } 

La classe proxy effectue maintenant notre recherche d'un chiffre binary et nous obligeons l'opérateur du tableau [] à renvoyer une instance du proxy disposant d'un access limité aux éléments internes du tableau.

Une classe proxy en C ++ est utilisée pour implémenter le modèle de proxy dans lequel un object est une interface ou un médiateur pour un autre object.

Une utilisation typique d’une classe proxy en C ++ consiste à implémenter l’opérateur [], car l’opérateur [] peut être utilisé pour obtenir des données ou pour définir des données dans un object. L’idée est de fournir une classe de proxy qui permette la détection d’une utilisation get de l’opérateur [] par rapport à l’utilisation des données définies par l’opérateur []. L’opérateur [] d’une classe utilise l’object proxy pour aider à effectuer la tâche appropriée en détectant si l’opérateur [] est utilisé pour obtenir ou définir des données dans l’object.

Le compilateur C ++ sélectionne les opérateurs et les opérateurs de conversion appropriés à partir de la classe cible fournie et des définitions de classe proxy afin de tirer un parti particulier du travail de l’opérateur [].

Cependant, une classe proxy a d’autres utilisations en C ++. Voir, par exemple, cet article sur les objects auto-enregistrés en C ++ de Dr. Dobbs qui décrit l’utilisation d’une classe proxy en tant que partie d’une fabrique d’objects. La fabrique d’objects fournit un type d’object particulier en fonction de certains critères, dans cet exemple un format d’image graphique. Chacun des différents convertisseurs d’image graphique est représenté par un object proxy.

Toutes ces exigences peuvent être satisfaites en utilisant un “magasin spécialisé” dans lequel il n’existe pas, au moment de la compilation, de code unique qui connaisse tous les formats pris en charge. La liste des objects pris en charge est générée au moment de l’exécution lorsque chaque object au format de fichier enregistre son existence avec un object de magasin spécialisé.

La construction d’un magasin spécialisé se compose de quatre éléments:

  • Chaque classe qui va dans le magasin sera représentée par une classe proxy. Le proxy sait comment créer des objects pour le magasin et fournit une interface standard pour les informations sur la classe.
  • Vous devez déterminer les critères que le magasin spécialisé exposera aux appelants, puis implémenter des interfaces pour ces critères dans le magasin, dans la classe proxy et dans la classe d’origine.
  • Toutes les classes de proxy dériveront d’une classe de base commune afin que le magasin spécialisé puisse les utiliser de manière interchangeable. Chaque classe de proxy sera implémentée en tant que modèle appelant des fonctions statiques dans la classe d’origine.
  • Les classes proxy seront automatiquement enregistrées au démarrage du programme en définissant une variable globale pour chaque classe proxy dont le constructeur enregistrera la classe proxy avec le magasin spécialisé.

Un autre exemple serait la façon dont les objects Microsoft DCOM (Dissortingbuted COM) utilisent un proxy sur la machine hôte d’un utilisateur de l’object DCOM pour représenter l’object réel qui réside sur une autre machine hôte. Le proxy fournit une interface pour l’object réel sur une machine différente et gère la communication entre l’utilisateur de l’object et l’object réel.

En résumé, un object proxy sert d’intermédiaire avec l’object réel. Un object proxy est utilisé chaque fois qu’il est nécessaire d’effectuer une conversion ou une transformation entre l’utilisateur d’un object et l’object réel avec une sorte d’indirection qui fournit un service permettant l’utilisation de l’object réel lorsqu’il existe un obstacle à son utilisation. l’object réel directement.

EDIT – Un exemple simple utilisant un proxy avec l’opérateur [] pour un simple magasin de données de tableaux

La source suivante utilise un object proxy pour l’opérateur [] d’une classe. La sortie du faisceau de test est fournie ci-dessous pour montrer la création et la destruction des différents objects proxy lorsque la classe proxy est utilisée pour accéder à la classe réelle et la manipuler. Il est instructif d’exécuter cela dans un débogueur pour le regarder s’exécuter.

 // proxy.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include  #include  class TArrayProxy; // The actual class which we will access using a proxy. class TArray { public: TArray(); ~TArray (); TArrayProxy operator [] (int iIndex); int operator = (TArrayProxy &j); void Dump (void); char m_TarrayName[4]; // this is the unique name of a particular object. static char TarrayName[4]; // This is the global used to create unique object names private: friend class TArrayProxy; // allow the proxy class access to our data. int iArray[10]; // a simple integer array for our data store }; // The proxy class which is used to access the actual class. class TArrayProxy { public: TArrayProxy(TArray *p = 0, int i=0); ~TArrayProxy(); TArrayProxy & operator = (int i); TArrayProxy & operator += (int i); TArrayProxy & operator = (TArrayProxy &src); operator int (); int iIndex; char m_TarrayproxyName[4]; // this is the unique name of a particular object. static char TarrayproxyName[4]; // This is the global used to create unique object names private: TArray *pArray; // pointer to the actual object for which we are a proxy. }; // initialize the object names so as to generate unique object names. char TArray::TarrayName[4] = {" AA"}; char TArrayProxy::TarrayproxyName[4] = {" PA"}; // Construct a proxy object for the actual object along with which particular // element of the actual object data store that this proxy will represent. TArrayProxy::TArrayProxy(TArray *p /* = 0 */, int i /* = 0 */) { if (p && i > 0) { pArray = p; iIndex = i; strcpy (m_TarrayproxyName, TarrayproxyName); TarrayproxyName[2]++; std::cout << " Create TArrayProxy " << m_TarrayproxyName << " iIndex = " << iIndex << std::endl; } else { throw "TArrayProxy bad p"; } } // The destructor is here just so that we can log when it is hit. TArrayProxy::~TArrayProxy() { std::cout << " Destroy TArrayProxy " << m_TarrayproxyName << std::endl; } // assign an integer value to a data store element by using the proxy object // for the particular element of the data store. TArrayProxy & TArrayProxy::operator = (int i) { pArray->iArray[iIndex] = i; std::cout << " TArrayProxy assign = i " << i << " to " << pArray->m_TarrayName << " using proxy " << m_TarrayproxyName << " iIndex " << iIndex << std::endl; return *this; } TArrayProxy & TArrayProxy::operator += (int i) { pArray->iArray[iIndex] += i; std::cout << " TArrayProxy add assign += i " << i << " to " << pArray->m_TarrayName << " using proxy " << m_TarrayproxyName << " iIndex " << iIndex << std::endl; return *this; } // assign an integer value that is specified by a proxy object to a proxy object for a different element. TArrayProxy & TArrayProxy::operator = (TArrayProxy &src) { pArray->iArray[iIndex] = src.pArray->iArray[src.iIndex]; std::cout << " TArrayProxy assign = src " << src.m_TarrayproxyName << " iIndex " << src.iIndex << " to " << m_TarrayproxyName << " iIndex "<< iIndex << " from" << std::endl; return *this; } TArrayProxy::operator int () { std::cout << " TArrayProxy operator int " << m_TarrayproxyName << " iIndex " << iIndex << " value of " << pArray->iArray[iIndex] << std::endl; return pArray->iArray[iIndex]; } TArray::TArray() { strcpy (m_TarrayName, TarrayName); TarrayName[2]++; std::cout << " Create TArray = " << m_TarrayName << std::endl; for (int i = 0; i < sizeof(iArray)/sizeof(iArray[0]); i++) { iArray[i] = i; } } // The destructor is here just so that we can log when it is hit. TArray::~TArray() { std::cout << " Destroy TArray " << m_TarrayName << std::endl; } TArrayProxy TArray::operator [] (int iIndex) { std::cout << " TArray operator [" << iIndex << "] " << m_TarrayName << std::endl; if (iIndex > 0 && iIndex <= sizeof(iArray)/sizeof(iArray[0])) { // create a proxy object for this particular data store element return TArrayProxy(this, iIndex); } else throw "Out of range"; } int TArray::operator = (TArrayProxy &j) { std::cout << " TArray operator = " << m_TarrayName << " from" << j.m_TarrayproxyName << " index " << j.iIndex << std::endl; return j.iIndex; } void TArray::Dump (void) { std::cout << std::endl << "Dump of " << m_TarrayName << std::endl; for (int i = 0; i < sizeof(iArray)/sizeof(iArray[0]); i++) { std::cout << " i = " << i << " value = " << iArray [i] << std::endl; } } // ----------------- Main test harness follows ---------------- // we will output the line of code being hit followed by the log of object actions. int _tmain(int argc, _TCHAR* argv[]) { TArray myObj; std::cout << std::endl << "int ik = myObj[3];" << std::endl; int ik = myObj[3]; std::cout << std::endl << "myObj[6] = myObj[4] = 40;" << std::endl; myObj[6] = myObj[4] = 40; std::cout << std::endl << "myObj[5] = myObj[5];" << std::endl; myObj[5] = myObj[5]; std::cout << std::endl << "myObj[2] = 32;" << std::endl; myObj[2] = 32; std::cout << std::endl << "myObj[8] += 20;" << std::endl; myObj[8] += 20; myObj.Dump (); return 0; } 

Et voici la sortie de cet exemple à partir d'une application console avec Visual Studio 2005.

  Create TArray = AA int ik = myObj[3]; TArray operator [3] AA Create TArrayProxy PA iIndex = 3 TArrayProxy operator int PA iIndex 3 value of 3 Destroy TArrayProxy PA myObj[6] = myObj[4] = 40; TArray operator [4] AA Create TArrayProxy PB iIndex = 4 TArrayProxy assign = i 40 to AA using proxy PB iIndex 4 TArray operator [6] AA Create TArrayProxy PC iIndex = 6 TArrayProxy assign = src PB iIndex 4 to PC iIndex 6 from Destroy TArrayProxy PC Destroy TArrayProxy PB myObj[5] = myObj[5]; TArray operator [5] AA Create TArrayProxy PD iIndex = 5 TArrayProxy operator int PD iIndex 5 value of 5 TArray operator [5] AA Create TArrayProxy PE iIndex = 5 TArrayProxy assign = i 5 to AA using proxy PE iIndex 5 Destroy TArrayProxy PE Destroy TArrayProxy PD myObj[2] = 32; TArray operator [2] AA Create TArrayProxy PF iIndex = 2 TArrayProxy assign = i 32 to AA using proxy PF iIndex 2 Destroy TArrayProxy PF myObj[8] += 20; TArray operator [8] AA Create TArrayProxy PG iIndex = 8 TArrayProxy add assign += i 20 to AA using proxy PG iIndex 8 Destroy TArrayProxy PG Dump of AA i = 0 value = 0 i = 1 value = 1 i = 2 value = 32 i = 3 value = 3 i = 4 value = 40 i = 5 value = 5 i = 6 value = 40 i = 7 value = 7 i = 8 value = 28 i = 9 value = 9 

Une classe proxy vous permet de masquer les données privées d’une classe aux clients de la classe.

Fournir aux clients de votre classe une classe proxy ne connaissant que l’interface publique de votre classe permet aux clients d’utiliser les services de votre classe sans lui donner access aux détails d’implémentation de votre classe.