c++ design-patterns policy-injection policy-based-design

Diseño basado en políticas y mejores prácticas-C++



design-patterns policy-injection (4)

struct InkPen { void Write() { this->WriteImplementation(); } void WriteImplementation() { std::cout << "Writing using a inkpen" << std::endl; } }; struct BoldPen { void Write() { std::cout << "Writing using a boldpen" << std::endl; } }; template<class PenType> class Writer : public PenType { public: void StartWriting() { PenType::Write(); } }; int main() { Writer<InkPen> writer; writer.StartWriting(); Writer<BoldPen> writer1; writer1.StartWriting(); return 0; }

Escribí el código anterior como parte de los diseños basados ​​en políticas de aprendizaje. Tengo algunas preguntas sobre el código anterior.

1 - ¿Se ve correcta esta implementación? Quiero decir: ¿realmente parece un diseño basado en políticas?

2 - Ahora puedo enganchar cualquier tipo de bolígrafos al escritor. ¿Pero qué haré cuando obtenga un lápiz sin un constructor predeterminado (solo constructores parametrizados)? ¿Cómo voy a manejar esta situación?

template<class PenType> class Writer : public PenType { public: void StartWriting() { PenType::Write(); } };

3 - Cuando el código anterior se usa como

Writer<InkPen> writer;

Supongo que el compilador reemplazará PenType con InkPen . En caso afirmativo, ¿por qué no puedo llamar simplemente Write () desde StartWriting () en lugar de prefijar el nombre de clase base ( PenType :: Write () )?

4 - Creo que el diseño basado en políticas lo obliga a derivar de clases que son semánticamente inválidas. En el código anterior, un escritor se deriva de una pluma solo porque el escritor usa una pluma. Pero decir que el escritor es una pluma es semánticamente inválido. ¿Hay alguna otra forma mejor de abordar esto o me estoy perdiendo algo aquí?

¿Alguna idea?


1 - ¿Es correcta esta implementación? Quiero decir, ¿es realmente un diseño basado en políticas?

Las clases de políticas derivan su utilidad de combinar comportamientos para producir una rica variedad de combinaciones. Cuando tiene un único parámetro de plantilla como este, no es una clase de política.

2 - Ahora puedo enganchar cualquier tipo de bolígrafos al escritor. ¿Pero qué haré cuando obtenga un lápiz sin un constructor predeterminado (solo constructores parametrizados)? ¿Cómo voy a manejar esta situación?

De nuevo, este es un ejemplo extraño de una clase de política. Sin embargo, para responder directamente a su pregunta, puede proporcionar un constructor que acepte PenType. Probablemente también debería evitar la herencia de PenType y almacenarlo como miembro (no es necesario que su clase de política esté unida a sus políticas).

Supongo que el compilador reemplazará PenType con InkPen. En caso afirmativo, ¿por qué no puedo llamar simplemente Write () desde StartWriting () en lugar de prefijar el nombre de clase base (PenType :: Write ())?

Cuando hereda de una plantilla de clase, debe especificar este-> miembro o BaseClass :: miembro.

4 - Creo que el diseño basado en políticas lo obliga a derivar de clases que son semánticamente inválidas. En el código anterior, un escritor se deriva de una pluma solo porque el escritor usa una pluma. Pero decir que el escritor es una pluma es semánticamente inválido. ¿Hay alguna otra forma mejor de abordar esto o me estoy perdiendo algo aquí?

Almacene PenType como miembro como se sugirió anteriormente. Siempre prefiera la composición a la herencia, ya que evita la estrecha relación de acoplamiento de la herencia.


Así es como yo implementaría la clase:

template<class PenType> class Writer { public: Writer(const PenType& pen = PenType()) : pen(pen) {} void StartWriting() { pen.Write(); } private: PenType pen; };

Esto le permite al usuario pasar un objeto Pen específico al constructor, si no tiene un constructor predeterminado, o si no quiere que se use, y segundo, todavía le permite omitir el objeto PenType si está feliz de dejar que cree uno con el constructor predeterminado. La biblioteca estándar de C ++ hace lo mismo en muchas clases (por ejemplo, piense en los asignadores para clases de contenedor).

Quité la herencia. Realmente no parece que agregue nada (y puede causar problemas. Probablemente no quiera que el usuario de la clase Writer llame directamente a la función PenType :: Write. En su lugar, podría usar la herencia privada, pero a menudo, la composición es una Diseño más sencillo y convencional.

En general, el diseño basado en políticas no requiere herencia. Agregarlo como miembro funciona igual de bien. Si va por herencia, hágalo en privado para que no tenga el problema que mencionó como el # 4.


Esto parece un buen ejemplo de implementación de puntero inteligente basada en políticas: link . Andrei Alexandrescu describe la implementación de punteros inteligentes basada en políticas en uno de sus libros. En cuanto a sus preguntas ahora. Tengo algo de experiencia en esto pero no lo suficiente como para dar por sentado mis palabras:

Anuncio 1 y 4. Supongo que el diseño basado en políticas tiene más que ver con las plantillas que con la herencia. Escribir una clase de plantilla y los argumentos de plantilla son clases de política, así:

template<class FooPolicy, class BarPolicy> class Baz { // implementation goes here };

Luego usas métodos de clases de políticas en tu clase:

void Baz::someMethod(int someArg) { FooPolicy::methodInit(); // some stuff BarPolicy::methodDone(); }

Utilizo métodos estáticos en este ejemplo porque a menudo la política no requiere ningún estado. Si lo hace, incorporas el estado de la política por composición, no por herencia:

template<class FooPolicy, class BarPolicy> class Baz { private: FooPolicy::State fooState; // might require ''typename'' keyword, I didn''t // actually tried this in any compiler // rest of the Baz class };

Anuncio 2. Puede escribir una especialización de plantilla: para una combinación particular de clase principal y políticas, puede escribir una versión especial de cualquier método o constructor, AFAIK:

template <> Baz<SomeConcreteFooPolicy, SomeConcreteBazPolicy>::Baz(someArgument) : fooState(someArgument) { // stuff here }

Espero que te ayude un poco,

Micro


Sé que este hilo es antiguo, pero hay una falla importante en la publicación inicial y este hilo es uno de los mejores resultados de Google ... así que:

¡No use public herencia public para el diseño basado en políticas! Esto diría que "is-a" en lugar de "has-a" / "uses-a". Por lo tanto, debe utilizar private herencia private !