2009-06-15 6 views
31

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

+1

Proxy (parmi beaucoup d'autres significations) est un modèle de conception ** ** - voir [wikipedia ] (http://en.wikipedia.org/wiki/Proxy_pattern) pour une excellente couverture (pas spécifiquement C++ spécifique, bien sûr). –

+0

tout à fait d'accord, il y a d'excellentes réponses fournies ici à cette question – vsoftco

Répondre

58

Un proxy est une classe qui fournit une interface modifiée à une autre classe. Voici un exemple - supposons que nous avons une classe de tableau que nous voulons seulement être en mesure de contenir les chiffres binaires 1 ou 0. Voici un premier essai:

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

Nous voulons opérateur [] pour se plaindre si nous disons quelque chose comme un [1] = 42, mais ce n'est pas possible parce que l'opérateur voit seulement l'index de dans le tableau, pas la valeur étant stockée.

Nous pouvons résoudre cela en utilisant un proxy:

#include <iostream> 
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 fait maintenant notre contrôle pour un chiffre binaire et nous faisons l'opérateur du tableau [] retourne une instance du proxy qui a un accès limité au les internes du tableau.

+1

Je ferais 'aproxy :: void operator = (int n)' retourner 'n' comme un' int', car il permettra de chaîner comme 'a [0 ] = a [1] = 0'. Sinon, vous copiez un 'aproxy' qui est retourné par' a [1] = 0' dans 'a [0]', et il fonctionne comme prévu. Autre que cela, réponse géniale et concise! – vsoftco

2

Une classe proxy vous permet de cacher les données privées d'une classe de clients de la classe.

Fournir aux clients de votre classe une classe proxy qui ne connait que l'interface publique de votre classe permet aux clients d'utiliser les services de votre classe sans donner au client l'accès aux détails d'implémentation de votre classe.

8

Une classe proxy en C++ est utilisée pour implémenter le Proxy Pattern dans lequel un objet est une interface ou un médiateur pour un autre objet.

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 objet. L'idée est de fournir une classe proxy qui permette la détection d'une utilisation de données get de l'opérateur [] par rapport à l'utilisation des données définies de l'opérateur []. L'opérateur [] d'une classe utilise l'objet proxy pour aider à faire la bonne chose en détectant si l'opérateur [] est utilisé pour obtenir ou définir des données dans l'objet.

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 de proxy afin de faire une utilisation particulière du travail de l'opérateur [].

Toutefois, il existe d'autres utilisations pour une classe proxy en C++. Par exemple, voir cet article sur Self-Registering Objects in C++ de Dr. Dobbs qui décrit l'utilisation d'une classe proxy dans le cadre d'une fabrique d'objets. La fabrique d'objets fournit un type d'objet 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 objet proxy.

Toutes ces exigences peuvent être satisfaites en utilisant un « magasin spécialisé » dans où il n'y a pas de place unique dans le code à la compilation qui sait sur tous les formats pris en charge. La liste des objets pris en charge est construite à durée d'exécution lorsque chaque objet au format de fichier enregistre son existence avec un objet magasin spécialisé .

Il y a quatre parties à la construction d'un magasin spécialisé:

  • Chaque classe qui va dans le magasin sera représentée par une classe proxy.Le proxy sait comment créer des objets pour le magasin et fournit une interface standard pour les informations sur la classe.
  • Vous devez décider quels critères 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 qui appelle des fonctions statiques dans la classe d'origine.
  • Les classes proxy seront enregistrées automatiquement au démarrage du programme en définissant une variable globale pour chaque classe proxy dont le constructeur enregistrera la classe proxy auprès du magasin spécialisé.

Un autre exemple serait comment les objets Microsoft DCOM (Distributed COM) utilisent un proxy sur la machine hôte d'un utilisateur de l'objet DCOM pour représenter l'objet réel qui se trouve sur une autre machine hôte. Le proxy fournit une interface pour l'objet réel sur une machine différente et gère la communication entre l'utilisateur de l'objet et l'objet réel. En résumé, un objet proxy est utilisé pour servir d'intermédiaire à l'objet réel. Un objet proxy est utilisé quand il doit y avoir une sorte de conversion ou de transformation entre l'utilisateur d'un objet et l'objet réel avec une sorte d'indirection qui fournit un service permettant l'utilisation de l'objet réel quand il y a un obstacle l'objet réel directement.

EDIT - Un exemple simple en utilisant un proxy avec l'opérateur [] pour un simple tableau de données magasin

La source suivante utilise un objet 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 divers objets proxy lorsque la classe proxy est utilisée pour accéder et manipuler la classe réelle. Il est instructif d'exécuter ceci dans un débogueur pour le regarder s'exécuter.

// proxy.cpp : Defines the entry point for the console application. 
// 

#include "stdafx.h" 
#include <string.h> 

#include <iostream> 

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 
Questions connexes