¿Qué es Proxy Class en C ++?

¿Qué es una clase Proxy en C ++? ¿Por qué se crea y dónde es útil?

Un proxy es una clase que proporciona una interfaz modificada a otra clase. Aquí hay un ejemplo: supongamos que tenemos una clase de matriz que solo queremos que contenga los dígitos binarios 1 o 0. Aquí hay un primer bash:

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

Queremos que el operador [] se queje si decimos algo como a [1] = 42, pero eso no es posible porque el operador solo ve el índice en el arreglo, no el valor que se almacena.

Podemos resolver esto usando 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 clase proxy hace ahora nuestra comprobación de un dígito binario y hacemos que el operador de la matriz [] devuelva una instancia del proxy que tiene acceso limitado a las partes internas de la matriz.

Una clase de proxy en C ++ se utiliza para implementar el patrón de proxy en el que un objeto es una interfaz o un mediador para algún otro objeto.

Un uso típico de una clase proxy en C ++ es la implementación del operador [] ya que el operador [] se puede usar para obtener datos o establecer datos dentro de un objeto. La idea es proporcionar una clase proxy que permita la detección de un uso de obtención de datos del operador [] frente al uso de datos establecidos del operador []. El operador [] de una clase usa el objeto proxy para ayudar a hacer lo correcto al detectar si el operador [] se está utilizando para obtener o establecer datos en el objeto.

El comstackdor de C ++ selecciona los operadores apropiados y los operadores de conversión de la clase objective proporcionada y las definiciones de la clase proxy para hacer un uso particular del trabajo del operador [].

Sin embargo, hay otros usos para una clase proxy en C ++. Por ejemplo, consulte este artículo sobre objetos autoregistrados en C ++ del Dr. Dobbs que describe el uso de una clase proxy como parte de una fábrica de objetos. La fábrica de objetos proporciona un tipo particular de objeto dependiendo de algunos criterios, en este ejemplo, un formato de imagen gráfica. Cada uno de los diferentes convertidores de imágenes gráficas está representado por un objeto proxy.

Todos estos requisitos se pueden cumplir mediante el uso de una “tienda especializada” en la que no hay un solo lugar en el código en el momento de la comstackción que conoce todos los formatos admitidos. La lista de objetos admitidos se genera en tiempo de ejecución cuando cada objeto de formato de archivo registra su existencia con un objeto de tienda especializada.

Hay cuatro partes para construir una tienda especializada:

  • Cada clase que va en la tienda estará representada por una clase proxy. El proxy sabe cómo crear objetos para la tienda y proporciona una interfaz estándar para obtener información sobre la clase.
  • Debe decidir qué criterios expondrá la tienda especializada a las personas que llaman, luego implementar las interfaces para esos criterios en la tienda, en la clase de proxy y en la clase original.
  • Todas las clases proxy se derivarán de una clase base común para que la tienda especializada pueda usarlas de manera intercambiable. Cada clase proxy se implementará como una plantilla que llama a funciones estáticas en la clase original.
  • Las clases proxy se registrarán automáticamente al inicio del progtwig al definir una variable global para cada clase proxy cuyo constructor registrará la clase proxy con el almacén especializado.

Otro ejemplo sería cómo los objetos Microsoft DCOM (COM distribuido) usan un proxy en la máquina host de un usuario del objeto DCOM para representar el objeto real que reside en otra máquina host. El proxy proporciona una interfaz para el objeto real en una máquina diferente y maneja la comunicación entre el usuario del objeto y el objeto real.

En resumen, un objeto proxy se usa para actuar como intermediario del objeto real. Un objeto proxy se utiliza cuando debe haber algún tipo de conversión o transformación entre el usuario de un objeto y el objeto real con algún tipo de indirección que proporciona un servicio que permite el uso del objeto real cuando hay algún obstáculo en el uso el objeto real directamente.

EDITAR – Un ejemplo simple usando un proxy con el operador [] para un simple almacén de datos de matriz

La siguiente fuente utiliza un objeto proxy para el operador [] de una clase. La salida del arnés de prueba se proporciona a continuación para mostrar la creación y destrucción de los diversos objetos proxy, ya que la clase proxy se usa para acceder y manipular la clase real. Es instructivo ejecutar esto en un depurador para verlo ejecutar.

 // 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; } 

Y aquí está el resultado de este ejemplo desde una aplicación de consola con 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 

Una clase proxy le permite ocultar los datos privados de una clase de los clientes de la clase.

Proporcionar a los clientes de su clase una clase proxy que solo conoce la interfaz pública de su clase permite a los clientes utilizar los servicios de su clase sin dar acceso al cliente a los detalles de implementación de su clase.