quarta-feira, 1 de julho de 2009

Ponteiro opaco

GCC Muitas vezes programadores de C++ e Java confundem encapsulamento com ocultação, o que não é a intenção da orientação a objetos.

No entanto, o contrário da ocultação, a exposição, também não é desejável para o encapsulamento, pois causa uma confusão entre interface e implementação.

Em C++, a declaração de uma classe força a exposição de sua estrutura privada. Um exemplo bem simples:
class X {
public:
X(int);
~X();

int get(void) const;

private:
int value;
}


Neste exemplo insanamente simples, é possível ver a estrutura privada da classe X, o que no caso não é um problema muito sério, mas em casos ligeiramente mais complexos, é possível corromper o encapsulamento.

Uma forma de resolver o problema é usando um design pattern¹ chamado ponteiro opaco ou Pimpl.

A ideia é que a classe de interface seja apenas uma moldura (wrapper) para a classe de implementação. A classe de implementação é declarada no corpo da classe de interface, mas nada é exposto ali.

A classe de implementação vai ser finalmente implementada no arquivo onde os métodos da classe de interface são implementados. Os métodos da classe de interface então simplemente chamam os métodos da classe de implementação.

Voltemos ao exemplo… o arquivo de cabeçalho de nossa classe (x.h por exemplo) passa a ser:
class X {
class Ximpl;

public:
X(int);
~X();

int get(void) const;

private:
Ximpl *pimpl;
}


Como é possível ver, nada da implementação está exposto.

Então a classe de implementação é finalmente definida no arquivo de implementação (x.cc):
class X::Ximpl {
public:
Ximpl(int);
~Ximpl();

int get(void) const;

private:
int value;

}


O resto do código fica:
X::X(int value): pimpl(new X::Ximpl(value)) {
// Nada mais a fazer
}

X::Ximpl::Ximpl(int value): value(value) {
// Nada mais a fazer
}

X::~X() {
delete this->pimpl;
}

X::Ximpl::~Ximpl() {
// Nada mais a fazer
}

X::get(void) const {
return this->pimpl->get();
}

X::Ximpl::get(void) const {
return this->value;
}


É claro que é um pattern extremamente desconfortável, pois é preciso implementar cada método duas vezes, uma para a classe de interface, outra para a classe de implementação… e há sim formas melhores de fazer isso.

Mas Pimpl funciona, é uma saída viável e pode salvar sua vida. =D

[update 2009-08-01]Sugestão de leitura de 0xc0de: Compilation Firewalls.[/update]

[]'s
Cacilhas, La Batalema


¹ Design patterns: antes que me crucifiquem por usar esta expressão fora do mundinho pequeno de Java, a definição de design pattern é «um jeito formal de documentar a solução para um problema de desenvolvimento», o que define corretamente ponteiro opaco.