Prototype (patrón de diseño)

De Wikipedia, la enciclopedia libre
Saltar a: navegación, búsqueda

Definición[editar]

El patrón de diseño Prototype (Prototipo), tiene como finalidad crear nuevos objetos duplicándolos, clonando una instancia creada previamente..

Este patrón especifica la clase de objetos a crear mediante la clonación de un prototipo que es una instancia ya creada. La clase de los objetos que servirán de prototipo deberá incluir en su interfaz la manera de solicitar una copia, que será desarrollada luego por las clases concretas de prototipos.

Motivación[editar]

Este patrón resulta útil en escenarios donde es preciso abstraer la lógica que decide qué tipos de objetos utilizará una aplicación, de la lógica que luego usarán esos objetos en su ejecución. Los motivos de esta separación pueden ser variados, por ejemplo, puede ser que la aplicación deba basarse en alguna configuración o parámetro en tiempo de ejecución para decidir el tipo de objetos que se debe crear. En ese caso, la aplicación necesitará crear nuevos objetos a partir de modelos. Estos modelos, o prototipos, son clonados y el nuevo objeto será una copia exacta de los mismos, con el mismo estado. Como decimos, esto resulta interesante para crear, en tiempo de ejecución, copias de objetos concretos inicialmente fijados, o también cuando sólo existe un número pequeño de combinaciones diferentes de estado para las instancias de una clase.

Dicho de otro modo, este patrón propone la creación de distintas variantes de objetos que nuestra aplicación necesite, en el momento y contexto adecuado. Toda la lógica necesaria para la decisión sobre el tipo de objetos que usará la aplicación en su ejecución se hace independiente, de manera que el código que utiliza estos objetos solicitará una copia del objeto que necesite. En este contexto, una copia significa otra instancia del objeto. El único requisito que debe cumplir este objeto es suministrar la funcionalidad de clonarse.

En el caso, por ejemplo, de un editor gráfico, podemos crear rectángulos, círculos, etc... como copias de prototipos. Estos objetos gráficos pertenecerán a una jerarquía cuyas clases derivadas implementarán el mecanismo de clonación.

Prototipo.png

Estructura[editar]

En la imagen podemos ver la estructura del patrón:

ProtipoEstructura.png

Participantes[editar]

Cliente: Es el encargado de solicitar la creación de los nuevos objetos a partir de los prototipos.

Prototipo Concreto: Posee características concretas que serán reproducidas para nuevos objetos e implementa una operación para clonarse.

Prototipo: Declara una interfaz para clonarse, a la que accede el cliente.

Colaboraciones[editar]

El cliente solicita al prototipo que se clone.

Consecuencias[editar]

Aplicar el patrón prototipo permite ocultar las clases producto (prototipos concretos) del cliente y permite que el cliente trabaje con estas clases dependientes de la aplicación sin cambios.

Además, hace posible añadir y eliminar productos en tiempo de ejecución al invocar a la operación clonar, lo que supone un método que proporciona una configuración dinámica de la aplicación.

Este patrón permite la especificación de nuevos objetos generando un objeto con valores por defecto sobre el que posteriormente se podrán aplicar cambios. La especificación de nuevos objetos también puede realizarse mediante la variación de su estructura. Reducción del número de subclases.

Desventajas[editar]

La jerarquía de prototipos debe ofrecer la posibilidad de clonar un elemento y esta operación puede no ser sencilla de implementar. Por otro lado, si la clonación se produce frecuentemente, el coste puede ser importante.

Otros detalles[editar]

Clonación profunda vs Clonación superficial[editar]

Entre las diferentes modalidades entre las que 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.

Profunda vs Superficial.png

Soporte en Java[editar]

En Java disponemos de la interfaz cloneable y del Object Clone() throws CloneNotSupportedException para llevar a cabo la implementación nuestro prototipo de manera compatible con los prototipos ya existentes en las librerías Java.

Negociador de Productos[editar]

Una modificación o derivación de este patrón es el conocido como Negociador de Productos (Product Trader), que se centra en el tratamiento de los prototipos cuando varios clientes trabajan sobre ellos. Este patrón incorpora un gestor, normalmente utilizando el patron singleton, que actúa sobre un conjunto de prototipos frente a los clientes.

Implementación[editar]

Esta es una solución en el lenguaje PHP. En este ejemplo tenemos una clase abstracta PrototipoLibro, con dos subclases concretas: PrototipoLibroPHP y PrototipoLibroSQL.

<?php
abstract class PrototipoLibro {
    protected $titulo;
    protected $tema;
    abstract function __clone();
    function obtenerTitulo() {
        return $this->titulo;
    }
    function establecerTitulo($tituloIn) {
        $this->titulo = $tituloIn;
    }
    function obtenerTema() {
        return $this->tema;
    }
}
 
class PrototipoLibroPHP extends PrototipoLibro {
    function __construct() {
        $this->tema = 'PHP';
    }
    function __clone() {
    }
}
 
class PrototipoLibroSQL extends PrototipoLibro {
    function __construct() {
        $this->tema = 'SQL';
    }
    function __clone() {
    }
}
 
  writeln('PRUEBA DEL PATRÓN PROTOTIPO');
  writeln('');
 
  $phpProto = new PrototipoLibroPHP();
  $sqlProto = new PrototipoLibroSQL();
 
  $libro1 = clone $sqlProto;
  $libro1->establecerTitulo('SQL para Gatos');
  writeln('Libro 1 tema: '.$libro1->obtenerTema());
  writeln('Libro 1 título: '.$libro1->obtenerTitulo());
  writeln('');
 
  $libro2 = clone $phpProto;
  $libro2->establecerTitulo('OReilly Learning PHP 5');
  writeln('Libro 2 tema: '.$libro2->obtenerTema());
  writeln('Libro 2 título: '.$libro2->obtenerTitulo());
  writeln('');
 
  $libro3 = clone $sqlProto;
  $libro3->establecerTitulo('OReilly Learning SQL');
  writeln('Libro 3 tema: '.$libro3->obtenerTema());
  writeln('Libro 3 título: '.$libro3->obtenerTitulo());
  writeln('');
 
  writeln('FIN PRUEBA PATRÓN PROTOTIPO');
 
  function writeln($line_in) {
    echo $line_in."<br/>";
  }
?>
 
PRUEBA PATRÓN PROTOTIPO
Libro 1 tema: SQL Libro 1 título: SQL Para Gatos
Libro 2 tema: PHP Libro 2 título: OReilly Learning PHP 5 
Libro 3 tema: SQL Libro 3 título: OReilly Learning SQL
FIN PRUEBA PATRÓN PROTOTIPO

Esta es una solución en el lenguaje C.Sharp de DotNet:

using System;
// "Prototipo"
abstract class Prototipo {
 
	private string _id;
 
	public Prototipo( string id ) {
		_id = id;
	}
 
	public string ID {
		get{ return _id; }
	}
 
	abstract public Prototipo Clone();
}
 
class ClaseConcreta1 : Prototipo {
	public ClaseConcreta1 ( string id ) : base ( id ) {}
 
	override public Prototipo Clone() {
		// Copia superficial
		return (Prototipo)this.MemberwiseClone();
	}
}
 
class Prototipo[[Client]] {
		ClaseConcreta1 p1 = new ClaseConcreta("Clone-I");
		ClaseConcreta1 c1 = (ClaseConcreta1) p1.Clone();
		Console.WriteLine( "Clonación: {0}", c1.ID );		
	}
}

Esta es otra implementación distinta en el lenguaje Java:

// Los productos deben implementar esta interface
public interface Producto implements Cloneable {
    Object clone();
    // Aquí van todas las operaciones comunes a los productos que genera la factoría
}
 
// Un ejemplo básico de producto
public class UnProducto implements Producto {
    private int atributo;
 
    public UnProducto(int atributo) {
        this.atributo = atributo;
    }
 
    public Object clone() {
        return new UnProducto(this.atributo);
    }
 
    public String toString() {
        return ((Integer)atributo).toString();
    }
}
 
// La clase encargada de generar objetos a partir de los prototipos
public class FactoriaPrototipo {
    private HashMap mapaObjetos;
    private String nombrePorDefecto;
 
    public FactoriaPrototipo() {
        mapaObjetos = new HashMap();
        // Se incluyen en el mapa todos los productos prototipo
        mapaObjetos.put("producto 1", new UnProducto(1));
    }
 
    public Object create() {
        return create(nombrePorDefecto);
    }
 
    public Object create(String nombre) {
        nombrePorDefecto = nombre;
        Producto objeto = (Producto)mapaObjetos.get(nombre);
        return objeto != null ? objeto.clone() : null;
    }
}
 
public class PruebaFactoria {
    static public void main(String[] args) {
        FactoriaPrototipo factoria = new FactoriaPrototipo();
        Producto producto = (Producto) factoria.create("producto 1");
        System.out.println ("Este es el objeto creado: " + producto);
    }
}