5.6.2. Reducir la recompilación

El gestor de proyectos de su entorno de programación provocará la recompilación de un fichero si éste se modifica, o si se modifica otro fichero del que depende, es decir, un fichero de cabecera que se haya incluido. Esto significa que cada vez que se haga un cambio en una clase, ya sea a la interfaz pública o a las declaraciones de los miembros privados, se provocará una recompilación de todo lo que incluya ese fichero de cabecera. Este efecto se conoce usualmente como el problema de la clase base frágil. Para un proyecto grande en sus comienzos esto puede ser un gran problema pues la implementación suele cambiar a menudo; si el proyecto es muy grande, el tiempo de las compilaciones puede impedir un ciclo de desarrollo rápido.

La técnica para resolver esto se llama a veces clases manejador o el «gato de Cheshire»[56]: toda la información sobre la implementación desaparece excepto un único puntero, la «sonrisa». El puntero apunta a una estructura cuya definición se encuentra en el fichero de implementación junto con todas las definiciones de las funciones miembro. Así, siempre que la interfaz no se cambie, el fichero de cabecera permanece inalterado. La implementación puede cambiar a su gusto, y sólo el fichero de implementación deberá ser recompilado y reenlazado con el proyecto.

Aquí hay un ejemplo sencillo que demuestra cómo usar esta técnica. El fichero de cabecera contiene sólo la interfaz pública y un único puntero a una clase especificada de forma incompleta:

//: C05:Handle.h
// Handle classes
#ifndef HANDLE_H
#define HANDLE_H

class Handle {
  struct Cheshire; // Class declaration only
  Cheshire* smile;
public:
  void initialize();
  void cleanup();
  int read();
  void change(int);
};
#endif // HANDLE_H ///:~

Listado 5.8. C05/Handle.h


Esto es todo lo que el programador cliente puede ver. La línea

struct Cheshire;

es una especificación de tipo incompleta o una declaración de clase (una definición de clase debe incluir el cuerpo de la clase). Le dice al compilador que Cheshire es el nombre de una estructura, pero no da detalles sobre ella. Esta información sólo es suficiente para crear un puntero a la estructura; no puede crear un objeto hasta que el cuerpo de la estructura quede definido. En esta técnica, el cuerpo de la estructura está escondido en el fichero de implementación:

//: C05:Handle.cpp {O}
// Handle implementation
#include "Handle.h"
#include "../require.h"

// Define Handle's implementation:
struct Handle::Cheshire {
  int i;
};

void Handle::initialize() {
  smile = new Cheshire;
  smile->i = 0;
}

void Handle::cleanup() {
  delete smile;
}

int Handle::read() {
  return smile->i;
}

void Handle::change(int x) {
  smile->i = x;
} ///:~

Listado 5.9. C05/Handle.cpp


Cheshire es una estructura anidada, así que se debe definir con resolución de ámbito:

struct Handle::Cheshire {

En Handle::initialize(), se solicita espacio de almacenamiento para una estructura Cheshire, y en Handle::cleanup() se libera ese espacio. Este espacio se usa en lugar de todos los datos que estarían normalmente en la sección privada de la clase. Cuando compile Handle.cpp, esta definición de la estructura estará escondida en el fichero objeto donde nadie puede verla. Si cambia los elementos de Cheshire, el único fichero que debe ser recompilado es Handle.cpp pues el fichero de cabecera permanece inalterado.

El uso de Handle es como el uso de cualquier clase: incluir la cabecera, crear objetos, y mandar mensajes.

//: C05:UseHandle.cpp
//{L} Handle
// Use the Handle class
#include "Handle.h"

int main() {
  Handle u;
  u.initialize();
  u.read();
  u.change(1);
  u.cleanup();
} ///:~

Listado 5.10. C05/UseHandle.cpp


La única cosa a la que el programador cliente puede acceder es a la interfaz pública, así que mientras la implementación sea lo único que cambie, el fichero anterior no necesita recompilarse. Así, aunque esto no es una ocultación perfecta de la implementación, es una gran mejora.



[56] Este nombre se le atribuye a John Carolan, uno de los pioneros del C++, y por supuesto, a Lewis Carroll. Esta técnica se puede ver también como una forma del patrón de diseño «puente», descrito en el Volumen 2.