El patrón
prototipo crea objetos nuevos mediante la clonación de uno o varios prototipos almacenados. El
patrón prototipo nos permite acelerar la creación de instancias muy
grandes, mejorar la carga dinámica de clases (copia de objetos), y mantiene un registro de partes identificables de una gran estructura
de datos que se pueden copiar sin saber la subclase de los que fueron creados.
- Ocultar clases concretas del cliente.
- Agregar y eliminar nuevas clases (a través de prototipos) en tiempo de ejecución.
- Mantener un número minímo de clases en el sistema.
- Adaptarse a los cambios en las estructuras de datos en tiempo de ejecución.
En la siguiente imagen podemos ver un diagrama UML de este patrón:
Participantes:
- Prototipo (IPrototipo): Declara la interface para clonarse.
- Prototipo concreto (PrototipoA, PrototipoB): Implementa la operación de
- clonarse.
- Cliente: Crea un nuevo objeto solicitándole al prototipo que se clone
Colaboraciones:
El cliente
solicita a un objeto prototipo que se replique (se clone), es decir, que se
cree una copia de si mismo.
Concecuencias:
- Oculta las clases del producto al cliente.
- Permite que el cliente trabaje con clases dependientes de la aplicación sin cambios en este.
- Especificación de nuevos objetos mediante el cambio de sus valores.
- Especificación de nuevos objetos mediante la variación de su estructura a través del cambio del prototipo de modo que el cliente no se vea afectado.
- Reducción del número de subclases. El patrón prototipo permite instanciar un nuevo clon a partir del prototipo en vez de pedir al método de fabricación que cree una nueva instancia, por lo tanto, no necesita una jerarquía de clases creadoras.
- Configuración dinámica de una aplicación. Es posible añadir y eliminar productos en tiempo de ejecución. Algunos entornos en tiempo de ejecución te permiten la carga de clases de forma dinámica. El patrón prototipo es la clave para explotar tales facilidades.
Clonación
profunda vs. Clonación superficial
Entre las
diferentes modalidades que se puede optar a la hora de implementar la
clonación de un objeto prototipo, cabe destacar dos maneras de realizar la
clonación: Superficial y Profunda.
En la
primera de ellas, un cambio sobre el objeto asociado con un clon afecta al
objeto original, porque los objetos relacionados son los mismos (es decir, la
clonación replica sólo el propio objeto y su estado, no sus asociaciones con
terceros objetos), mientras que en la clonación profunda se clonan los objetos
y también sus objetos relacionados.
Nota: ejemplo tomado de wikipedia: Prototype (Patrón de diseño)
Características de clonación y serialización en C#
MemberwiseClone es un
método que está disponible en todos los objetos. Copia los valores de todos campos
y cualquier referencia y devuelve una referencia a la presente copia. Sin
embargo, no hace copia de las referencias el objeto. Es decir, se
realiza lo que se conoce como una copia superficial. Muchos objetos son
simples, sin referencias a otros objetos, y por lo tanto las copias
superficiales son adecuadas. A fin de preservar el valor completo del objeto,
incluyendo todos sus subobjetos utilizar
una copia profunda
No es fácil
escribir un algoritmo general para seguir todos los eslabones de una estructura
y recrear la disposición de otras partes. Sin embargo, los algoritmos existen,
y en el Framework. NET se encapsulan en un proceso llamado serialización. Los
objetos se copian a un determinado destino y puede ser devuelto a nuestra
voluntad. Las opciones de destino para la serialización son varias, incluidos
los discos e Internet, pero la más fácil para serializar objetos pequeños es
la misma memoria. Así, una copia en profundidad consiste en serializar y
deserializar en un método.
Un método
genérico que funcione para todos los tipos de datos (List<T>) debe ser marcado como serializable. Esto lo logramos
escribiendo sobre el nombre del atributo o método la palabra [Serializable ()]
Importante: La serialización es parte del Framework. NET, no del lenguaje C#.
NOTA:
La serialización de una estructura de objetos es posible sólo
si todos los objetos referenciados son serializable. Evite serializar un
objeto que tiene una referencia a un "Recurso", como un controlador de archivo
abierto o una conexión de base de datos.
|
Implementación:
Implementación de la opración de clonación:
Iniciación del objeto clonado:
Uso de un gestor de prototipos:
En este patrón surge el problema de quién crea los
prototipos para proporcionárselos al cliente de modo que este no sea
dependiente de las jerarquías. En general debe ser un objeto que tenga que
conocer la jerarquía y se la proporcione a los clientes (puede haber más de
uno), este objeto se conoce como el gestor de prototipos. El gestor de
prototipos tiene que proporcionar al cliente métodos para que el cliente
seleccione el prototipo que estime oportuno, por ejemplo un método Producto
crea (String tipoObjeto, otra Información).
Si en lugar de pasar un simple String
con un nombre se establecen especificaciones más complejas, el patrón prototipo degenerará
un Negociador de productos en donde el negociador es un gestor de
prototipos.Implementación de la opración de clonación:
En los objetos compuestos esto puede ser un problema. Por
ejemplo, si clonamos un objeto decorado (ver patrón decorador) de qué se hace
copia del envoltorio exterior o de todo el conjunto, es decir, copia las
referencias o crea nuevos objetos para el estado. En función de la decisión que
se tome se modificará la operación clonar().
Iniciación del objeto clonado:
Inicialización del objeto clonado en caso de que la réplica
necesite una inicialización adicional. A veces no es factible crear la
instancia y replicar todo el estado del objeto que sirve como prototipo. Por
ejemplo, en el caso de que el prototipo tenga una clave o identificador de
instancia o si hay una cierta parte del prototipo que no se puede clonar.
Cuando se requiere de una inicialización la opción más común puede ser:
Exportar a la superclase los métodos de inicialización. Esta
opción es válida si todas las figuras en la jerarquía tuviesen la misma interface
para realizar su inicialización. Ejemplo: void inicializar (string fich_xml).
Cada subclase debe redefinir el método y extraer la información necesaria.
Patrones relacionados:
- Composite: El patrón Prototype es utilizado a menudo con el patrón Composite.
- Abstract Factory: El patrón Abstract Factory puede ser una buena alternativa al patrón
- Facade: La clase cliente normalmente actúa comúnmente como un Facade que separa las otras clases que participan en el patrón Prototype del resto del programa.
- Factory Method: El patrón Factory Method puede ser una alternativa al patrón Prototype cuando la paleta de objetos prototipo nunca contiene más de un objeto.
- Decorador: El patrón Prototype es utilizado a menudo con el patrón Decorator.