La vista OO común es que una interfaz establece un contrato único que define cómo se utilizan los objetos que se ajustan a esa interfaz y cómo se comportan. El idioma NVI o patrón, que nunca se sabe cuando uno se convierte en el otro, se propone un cambio en la mentalidad de dividir la interfaz en dos contratos separados:
- forma en que la interfaz es para usarse
- lo que las clases derivadas deben ofrecer
Esto es, en cierto sentido particular a C++ (de hecho a cualquier idioma con la herencia múltiple), donde la interfaz de puede de hecho contener código que se adapta a la interfaz externa --¿Cómo usuarios ven me-- y la interfaz interna - cómo estoy implementado.
Esto puede ser útil en casos diferentes, primero cuando el comportamiento es común pero se puede parametrizar solo de manera específica, con un esqueleto de algoritmo común. Entonces el algoritmo puede implementarse en la clase base y los puntos de extensión en los elementos derivados. En idiomas sin herencia múltiple, esto debe implementarse dividiéndolo en una clase que implemente el algoritmo basado en algunos parámetros que cumplan con una interfaz 'privada' diferente. Estoy usando aquí 'privado' en el sentido de que solo tu clase usará esa interfaz.
El segundo uso común es que mediante el uso del idioma NVI, es simple para instrumentar el código sólo por la modificación en el nivel de base:
class Base {
public:
void foo() {
foo_impl();
}
private:
virtual void foo_impl() = 0;
};
El coste adicional de tener que escribir el despachador foo() { foo_impl(); }
es bastante pequeño y le permite agregar más adelante un mecanismo de bloqueo si convierte el código en una aplicación multiproceso, agrega el registro a cada llamada, o un temporizador para verificar cuánto implementaciones diferentes toman en cada función ... Dado que el método real que se implementa en las clases derivadas es privado en este nivel, se le garantiza que todas las llamadas polimórficas se pueden instrumentar en un único punto: la base (esto no bloquea la extensión de clases desde haciendo foo_impl
pensamiento pública)
void Base::foo() {
scoped_log log("calling foo"); // we can add traces
lock l(mutex); // thread safety
foo_impl();
}
Si los métodos virtuales fueron públicas, entonces no podría interceptar todas las llamadas a los métodos y tendría que agregar que la tala y el hilo de seguridad para todos las clases derivadas que implementan la interfaz .
interfaz! = Miembros públicos. Su interfaz es su _documentation_, no su archivo de encabezado. –