Articulo de referencia

Patrón prototipo

El patrón prototipo es un patrón de diseño creacional en el desarrollo de software . Se utiliza cuando el tipo de objetos a crear está determinado por una instancia prototípica ...

El patrón prototipo es un patrón de diseño creacional en el desarrollo de software . Se utiliza cuando el tipo de objetos a crear está determinado por una instancia prototípica , que se clona para producir nuevos objetos. Este patrón se emplea para evitar la creación de subclases de un creador de objetos en la aplicación cliente, como ocurre con el patrón método de fábrica , y para evitar el coste inherente de crear un nuevo objeto de la forma estándar (por ejemplo, utilizando la palabra clave ` new` ) cuando resulta prohibitivo para una aplicación determinada.

Para implementar este patrón, el cliente declara una clase base abstracta que especifica un método virtual puro ` clone()` . Cualquier clase que necesite la capacidad de un constructor polimórfico deriva de la clase base abstracta e implementa la operación `clone()` .

En lugar de escribir código que invoque el operador "new" en un nombre de clase codificado, el cliente llama al método clone() en el prototipo, llama a un método de fábrica con un parámetro que designa la clase derivada concreta deseada, o invoca el método clone() a través de algún mecanismo proporcionado por otro patrón de diseño.

La división mitótica de una célula —que da como resultado dos células idénticas— es un ejemplo de un prototipo que desempeña un papel activo en su propia copia y, por lo tanto, demuestra el patrón de prototipo. Cuando una célula se divide, se obtienen dos células con genotipo idéntico. En otras palabras, la célula se clona a sí misma. [ 1 ]

Descripción general

El patrón de diseño Prototype es uno de los 23 patrones de diseño del Gang of Four que describen cómo resolver problemas de diseño recurrentes para diseñar software orientado a objetos flexible y reutilizable, es decir, objetos que son más fáciles de implementar, modificar, probar y reutilizar. [ 2 ] : 117

El patrón de diseño de prototipos resuelve problemas como: [ 3 ]

  • ¿Cómo se pueden crear objetos de manera que se pueda determinar el tipo específico de objeto en tiempo de ejecución?
  • ¿Cómo se pueden instanciar las clases cargadas dinámicamente?

Crear objetos directamente dentro de la clase que los requiere (utiliza) es inflexible porque compromete la clase a objetos particulares en tiempo de compilación y hace imposible especificar qué objetos crear en tiempo de ejecución.

El patrón de diseño de prototipos describe cómo resolver este tipo de problemas:

  • Define un Prototypeobjeto que devuelva una copia de sí mismo.
  • Crea nuevos objetos copiando un Prototypeobjeto.

Esto permite configurar una clase con diferentes Prototypeobjetos, que se copian para crear nuevos objetos, e incluso más, Prototypese pueden agregar y eliminar objetos en tiempo de ejecución. Consulte también el diagrama de clases y secuencia UML a continuación.

Estructura

Diagrama de clases y secuencia UML

Un ejemplo de diagrama de clases y secuencia UML para el patrón de diseño Prototype.

En el diagrama de clases UML anterior , la clase se refiere a la interfaz para clonar un . La clase implementa la interfaz creando una copia de sí misma. El diagrama de secuencia UML muestra las interacciones en tiempo de ejecución: El objeto llama a un objeto, que crea y devuelve una copia de sí mismo (un objeto).ClientPrototypeProductProduct1PrototypeClientclone()prototype:Product1product:Product1

Diagrama de clases UML

Diagrama de clases UML que describe el patrón de diseño del prototipo

Reglas generales

A veces, los patrones de creación se superponen: hay casos en los que tanto el prototipo como la fábrica abstracta serían apropiados. En otras ocasiones, se complementan: la fábrica abstracta podría almacenar un conjunto de prototipos a partir de los cuales clonar y devolver objetos de producto. [ 2 ] : 126 La fábrica abstracta, el constructor y el prototipo pueden usar singletons en sus implementaciones. [ 2 ] : 81, 134 Las clases de fábrica abstracta a menudo se implementan con métodos de fábrica (creación por herencia ), pero también pueden implementarse usando prototipos (creación por delegación ). [ 2 ] : 95

A menudo, los diseños comienzan utilizando el método de fábrica (menos complicado, más personalizable, con proliferación de subclases) y evolucionan hacia la fábrica abstracta, el prototipo o el constructor (más flexible, más complejo) a medida que el diseñador descubre dónde se necesita mayor flexibilidad. [ 2 ] : 136

El prototipo no requiere herencia, pero sí requiere una operación de "inicialización". El método de fábrica requiere herencia, pero no requiere inicialización. [ 2 ] : 116

Los diseños que hacen un uso intensivo de los patrones compuestos y decorativos a menudo también pueden beneficiarse de Prototype. [ 2 ] : 126

Una pauta general en programación sugiere usar el clone()método `clone` al crear un objeto duplicado durante la ejecución para asegurar que refleje con precisión el objeto original. Este proceso, conocido como clonación de objetos, produce un nuevo objeto con atributos idénticos al que se está clonando. Alternativamente, instanciar una clase usando la newpalabra clave `clone` genera un objeto con valores de atributos predeterminados.

Por ejemplo, al diseñar un sistema para gestionar transacciones bancarias, puede ser necesario duplicar el objeto que contiene la información de la cuenta para realizar transacciones conservando los datos originales. En tales casos, clone()es preferible emplear este método en lugar newde instanciar un nuevo objeto.

Ejemplo

Ejemplo de C++23

Esta implementación en C++23 se basa en la implementación anterior a C++98 que aparece en el libro. En las anotaciones de C++ se incluye una explicación del patrón de diseño, junto con un ejemplo ilustrativo completo que utiliza el diseño de clases polimórficas .

importar std ;using std :: array ; using std :: shared_ptr ; using std :: unique_ptr ; using std :: vector ;clase enumeración Dirección : char { NORTE , SUR , ESTE , OESTE };clase MapSite { public : virtual void enter () = 0 ; virtual unique_ptr <MapSite> clone ( ) const = 0 ; virtual ~ MapSite () = default ; } ;clase Habitación : público Mapa { privado : int númeroHabitación ; shared_ptr < array < shared_ptr < Mapa > , 4 >> lados ; público : explicit Habitación ( int n = 0 ) : númeroHabitación { n }, lados { std :: make_shared < array < shared_ptr < Mapa > , 4 >> ()} {}~ Habitación () = predeterminado ;Habitación & setSide ( Dirección d , shared_ptr < MapSite > ms ) { ( * lados )[ static_cast < size_t > ( d )] = std :: move ( ms ); std :: println ( "Habitación::setSide {} ms" , d ); return * this ; }virtual void enter () sobrescribir {}virtual unique_ptr < MapSite > clone () const override { return std :: make_unique < Room > ( * this ); }Habitación ( const Habitación & ) = eliminar ; Habitación & operador = ( const Habitación & ) = eliminar ; };clase Muro : público MapSite { público : Muro () : MapSite () {}~ Pared () = predeterminado ;virtual void enter () sobrescribir {}[[ nodiscard ]] virtual unique_ptr < MapSite > clone () const override { return std :: make_unique < Wall > ( * this ); } };clase Puerta : público MapSite { privado : shared_ptr < Habitación > habitación1 ; shared_ptr < Habitación > habitación2 ; público : puerta explícita ( shared_ptr < Habitación > r1 = nullptr , shared_ptr < Habitación > r2 = nullptr ) : MapSite (), habitación1 { std :: mover ( r1 )}, habitación2 { std :: mover ( r2 )} {}~ Puerta () = predeterminado ;virtual void enter () sobrescribir {}[[ nodiscard ]] virtual unique_ptr < MapSite > clone () const override { return std :: make_unique < Door > ( * this ); }void initialize ( shared_ptr <Room> r1 , shared_ptr <Room> r2 ) { room1 = std :: move ( r1 ) ; room2 = std :: move ( r2 ) ; }Puerta ( const Puerta & ) = eliminar ; Puerta & operador = ( const Puerta & ) = eliminar ; };clase Laberinto { privado : vector < shared_ptr < Room >> habitaciones ; público : Laberinto () = predeterminado ; ~ Laberinto () = predeterminado ;Maze & addRoom ( shared_ptr < Room > r ) { std :: println ( "Maze::addRoom {}" , reinterpret_cast < void *> ( r . get ())); rooms . push_back ( std :: move ( r )); return * this ; }[[ nodiscard ]] shared_ptr < Room > roomNo ( int n ) const { for ( const Room & r : rooms ) { // lógica de búsqueda real aquí... } return nullptr ; }[[ nodiscard ]] virtual unique_ptr < Maze > clone () const { return std :: make_unique < Maze > ( * this ); } };clase MazeFactory { public : MazeFactory () = default ;virtual ~ MazeFactory () = predeterminado ;[[ nodiscard ]] virtual unique_ptr < Maze > makeMaze () const { return std :: make_unique < Maze > (); }[[ nodiscard ]] virtual shared_ptr < Wall > makeWall () const { return std :: make_shared < Wall > (); }[[ nodiscard ]] virtual shared_ptr < Room > makeRoom ( int n ) const { return std :: make_shared < Room > ( n ); }[[ nodiscard ]] virtual shared_ptr < Door > makeDoor ( shared_ptr < Room > r1 , shared_ptr < Room > r2 ) const { return std :: make_shared < Door > ( std :: move ( r1 ), std :: move ( r2 )); } };clase MazePrototypeFactory : public MazeFactory { private : unique_ptr < Maze > prototypeMaze ; shared_ptr < Room > prototypeRoom ; shared_ptr < Wall > prototypeWall ; shared_ptr < Door > prototypeDoor ; public : MazePrototypeFactory ( unique_ptr < Maze > m , shared_ptr < Wall > w , shared_ptr < Room > r , shared_ptr < Door > d ) : MazeFactory (), prototypeMaze { std :: move ( m )}, prototypeRoom { std :: move ( r )}, prototypeWall { std :: move ( w )}, prototypeDoor { std :: move ( d )} {}~ MazePrototypeFactory () = default ;virtual unique_ptr < Maze > makeMaze () const override { return prototypeMaze -> clone (); }[[ nodiscard ]] virtual shared_ptr < Room > makeRoom ( int n ) const override { return prototypeRoom -> clone (); }[[ nodiscard ]] virtual shared_ptr < Wall > makeWall () const override { return prototypeWall -> clone (); }[[ nodiscard ]] virtual shared_ptr < Door > makeDoor ( shared_ptr < Room > r1 , shared_ptr < Room > r2 ) const override { shared_ptr < Door > door = prototypeDoor -> clone (); door -> initialize ( std :: move ( r1 ), std :: move ( r2 )); return door ; }MazePrototypeFactory ( const MazePrototypeFactory & ) = delete ; MazePrototypeFactory & operator = ( const MazePrototypeFactory & ) = delete ; };clase MazeGame { public : MazeGame () = default ; ~ MazeGame () = default ;[[ nodiscard ]] unique_ptr < Maze > createMaze ( MazePrototypeFactory & factory ) { unique_ptr < Maze > maze = factory . makeMaze (); shared_ptr < Room > r1 = factory . makeRoom ( 1 ); shared_ptr < Room > r2 = factory . makeRoom ( 2 ); shared_ptr < Door > door = factory . makeDoor ( r1 , r2 );laberinto -> agregarHabitación ( std :: mover ( r1 )) . agregarHabitación ( std :: mover ( r2 ));r1 -> setSide ( Dirección :: NORTE , fábrica . makeWall ()) . setSide ( Dirección :: ESTE , puerta ) . setSide ( Dirección :: SUR , fábrica . makeWall ()) . setSide ( Dirección :: OESTE , fábrica . makeWall ());r2 -> setSide ( Dirección :: NORTE , fábrica . makeWall ()) . setSide ( Dirección :: ESTE , fábrica . makeWall ()) . setSide ( Dirección :: SUR , fábrica . makeWall ()) . setSide ( Dirección :: OESTE , puerta );devolver laberinto ; } };int main ( int argc , char * argv []) { MazeGame game ; MazePrototypeFactory simpleMazeFactory ( std :: make_unique < Maze > (), std :: make_shared < Wall > (), std :: make_shared < Room > ( 0 ), std :: make_shared < Door > () );unique_ptr < Maze > laberinto = juego . crearLaberinto ( simpleMazeFactory ); }

La salida del programa es:

Laberinto :: agregarHabitación 0x1160f50 Laberinto :: agregarHabitación 0x1160f70 Habitación :: establecerLado 0 0x11613c0 Habitación :: establecerLado 2 0x1160f90 Habitación :: establecerLado 1 0x11613e0 Habitación :: establecerLado 3 0x1161400 Habitación :: establecerLado 0 0x1161420 Habitación :: setLado 2 0x1161440 Habitación :: setLado 1 0x1161460 Habitación :: setLado 3 0x1160f90

Véase también

Referencias

  1. Duell, Michael (julio de 1997). "Ejemplos de patrones de diseño que no son software". Object Magazine . 7 (5): 54. ISSN 1055-3614 . 
  2. 1 2 3 4 5 6 7 Gamma, Erich ; Helm, Richard; Johnson, Ralph ; Vlissides, John (1994). Patrones de diseño: Elementos de software orientado a objetos reutilizable . Addison-Wesley. ISBN 0-201-63361-2.
  3. "El patrón de diseño Prototype: problema, solución y aplicabilidad" . w3sDesign.com . Consultado el 17 de agosto de 2017 .