Skip to content

Políticas y clases de políticas

Andres Angarita edited this page Dec 28, 2019 · 1 revision

Las políticas y clases de políticas ayudan a implementar elementos de diseño seguros, eficientes y altamente personalizables. Una política define una interfaz de clase o una interfaz de plantilla de clase. La interfaz consta de uno o todos los siguientes: definiciones de tipo interno, funciones miembro y variables miembro.

Las políticas tienen mucho en común con los rasgos (Alexandrescu 2000a) pero difieren en que ponen menos énfasis en el tipo y más énfasis en el comportamiento. Además, las políticas recuerdan el patrón de diseño de la Estrategia (Gamma et al. 1995), con el giro de que las políticas están limitadas en el tiempo de compilación.

Por ejemplo, definamos una política para crear objetos. La política Creator prescribe una plantilla de clase de tipo T. Esta plantilla de clase debe exponer una función miembro llamada Create que no toma argumentos y devuelve un puntero a T. Semánticamente, cada llamada a Create debe devolver un puntero a un nuevo objeto de tipo T. El modo exacto en el que se crea el objeto se deja a la latitud de la implementación de lo política.

Definamos algunas clases de políticas que implementan la política Creator. Una forma posible es usar el nuevo operador. Otra forma es usar malloc y una llamada al nuevo operador de colocación (Meyers 1998b). Otra forma sería crear objetos clonando un objeto prototipo. Aquí hay ejemplos de los tres métodos:

template <class T>
struct OperatorNewCreator {
	static T* Create() {
		return new T;
	}
};

template <class T>
struct MallocCreator {
	static T* Create() {
		void* buf = std::malloc(sizeof(T));
		if (!buf) return 0;
		return new(buf) T;
	}
};

template <class T>
struct PrototypeCreator {
	PrototypeCreator(T* pObj = 0) : pPrototype_(pObj) {}
	T* Create()	{
		return pPrototype_ ? pPrototype_->Clone() : 0;
	}
	T* GetPrototype() { return pPrototype_; }
	void SetPrototype(T* pObj) { pPrototype_ = pObj; }
private:
	T* pPrototype_;
};

Para una política dada, puede haber un número ilimitado de implementaciones. Las implementaciones de una política se denominan clases de políticas. Las clases de políticas no están destinadas a un uso independiente; en cambio, son heredados o contenidos dentro de otras clases.

Un aspecto importante es que, a diferencia de las interfaces clásicas (colecciones de funciones virtuales puras), las interfaces de las políticas están poco definidas. Las políticas están orientadas a la sintaxis, no a la firma. En otras palabras, Creator especifica que construcciones sintácticas deberían ser válidas para una clase conforme, en lugar de que funciones exactas deben implementar esa clase. Por ejemplo, la política Creator no especifica que Create debe ser estático o virtual; el único requisito es que la plantilla de clase defina una función miembro Create. Además, Creator dice que Create debería devolver un puntero a un nuevo objeto. En consecuencia, es aceptable que, en casos especiales, Create devuelva cero o arroje una excepción.

Puede implementar varias clases de políticas para una política determinada. Todos deben respetar la interfaz definida por la política. El usuario luego elige que clase de política usar en estructuras más grandes, como verá.

Las tres clases de políticas definidas anteriormente tienen implementaciones diferentes e incluso interfaces ligeramente diferentes (por ejemplo, PrototypeCreator tiene dos funciones adicionales: GetPrototype y SetPrototype). Sin embargo, todos definen una función llamada Create con el tipo de retorno requerido, por lo que se ajustan a la política de Creator.

Veamos ahora como podemos diseñar una clase que explote la política Creator. Dicha clase contendrá o heredará una de las tres clases definidas anteriormente, como se muestra a continuación:

// Library code
template <class CreationPolicy>
class WidgetManager : public CreationPolicy {
	...
};

Las clases que usan una o más políticas se denominan host o clases de host. En el ejemplo anterior, WidgetManager es una clase de host con una política. Los anfitriones son responsables de esamblar las estructuras y comportamientos de sus políticas en una sola unidad compleja.

Al crear instancias de la plantilla WidgetManager, el cliente pasa la política deseada:

// Application code
typedef WidgetManager< OperatorNewCreator<Widget> > MyWidgetMgr;

Analicemos el contexto resultante. Siempre que un objeto de tipo MyWidgetMgr necesita crear un Widget, invoca Create() para su subobjeto de política OperatorNewCreator<Widget>. Sin embargo, es el usuario de MyWidgetMgr quien elige la política de creación. Efectivamente, a través de su diseño, MyWidgetMgr permite a sus usuarios configurar un aspecto especifico de la funcionalidad de MyWidgetMgr.

Esta es la esencia del diseño de clases basado en políticas.