Sie sind auf Seite 1von 48

Creacion

clase
Factory Method (patrón de diseño)
En diseño de software, el patrón de diseño Factory Method consiste en utilizar una clase
constructora (al estilo del Abstract Factory) abstracta con unos cuantos métodos definidos y
otro(s) abstracto(s): el dedicado a la construcción de objetos de un subtipo de un tipo
determinado. Es una simplificación del Abstract Factory, en la que la clase abstracta tiene
métodos concretos que usan algunos de los abstractos; según usemos una u otra hija de esta
clase abstracta, tendremos uno u otro comportamiento.

Estructura[editar]
Las clases principales en este patrón son el creador y el producto. El creador necesita crear
instancias de productos, pero el tipo concreto de producto no debe ser forzado en las
subclases del creador, porque las posibles subclases del creador deben poder especificar
subclases del producto para utilizar.

La solución para esto es hacer un método abstracto (el método de la fábrica) que se define en
el creador. Este método abstracto se define para que devuelva un producto. Las subclases del
creador pueden sobrescribir este método para devolver subclases apropiadas del producto...

Ejemplo de código (en Java)[editar]

abstract class Creator{


// Definimos método abstracto
public abstract Product factoryMethod();
}
Ahora definimos el creador concreto:

public class ConcreteCreator extends Creator{


public Product factoryMethod() {
return new ConcreteProduct();
}
}

Definimos el producto y su implementación concreta:

public interface Product{


public void operacion();
}

public class ConcreteProduct implements Product{


public void operacion(){
System.out.println("Una operación de este producto");
}
}

Ejemplo de uso:

public static void main(String args[]){


Creator aCreator;
aCreator = new ConcreteCreator();
Product producto = aCreator.factoryMethod();
producto.operacion();
}
OBJETO

Abstract Factory
(Redirigido desde «Abstract Factory (patrón de diseño)»)

Abstract Factory (Fábrica Abstracta) es un patrón de diseño para el desarrollo de software.

Contexto y problema[editar]
Contexto: Debemos crear diferentes objetos, todos pertenecientes a la misma familia. Por
ejemplo: las bibliotecas para crear interfaces gráficas suelen utilizar este patrón y cada familia
sería un sistema operativo distinto. Así pues, el usuario declara un Botón, pero de forma más
interna lo que está creando es un BotónWindows o un BotónLinux, por ejemplo.
El problema que intenta solucionar este patrón es el de crear diferentes familias de objetos.
El patrón Abstract Factory está aconsejado cuando se prevé la inclusión de nuevas familias
de productos, pero puede resultar contraproducente cuando se añaden nuevos productos o
cambian los existentes, puesto que afectaría a todas las familias creadas.

Aspecto estático[editar]

La estructura típica del patrón Abstract Factory es la siguiente:


 Cliente: La clase que llamará a la factoría adecuada ya que necesita crear uno de los
objetos que provee la factoría, es decir, Cliente lo que quiere es obtener una instancia de
alguno de los productos (ProductoA, ProductoB).

 AbstractFactory: Es la definición de la interfaces de las factorías. Debe de proveer un


método para la obtención de cada objeto que pueda crear. ("crearProductoA()" y
"crearProductoB()")

 Factorías Concretas: Estas son las diferentes familias de productos. Provee de la


instancia concreta de la que se encarga de crear. De esta forma podemos tener una
factoría que cree los elementos gráficos para Windows y otra que los cree para Linux,
pudiendo poner fácilmente (creando una nueva) otra que los cree para MacOS, por
ejemplo.

 Producto abstracto: Definición de las interfaces para la familia de productos genéricos.


En el diagrama son "ProductoA" y "ProductoB". En un ejemplo de interfaces gráficas
podrían ser todos los elementos: Botón, Ventana, Cuadro de Texto, Combo... El cliente
trabajará directamente sobre esta interfaz, que será implementada por los diferentes
productos concretos.

 Producto concreto: Implementación de los diferentes productos. Podría ser por ejemplo
"BotónWindows" y "BotónLinux". Como ambos implementan "Botón" el cliente no sabrá si
está en Windows o Linux, puesto que trabajará directamente sobre la superclase o
interfaz.

Un ejemplo[editar]
Veremos un ejemplo didáctico y basado en el libro Head First Design Patterns, de O'Reilly.
Supongamos que disponemos de una cadena de pizzerías. Para crear pizzas disponemos de
un método abstracto en la clase Pizzería que será implementada por cada subclase de
Pizzería.

abstract Pizza crearPizza()

Concretamente se creará una clase PizzeríaZona por cada zona, por ejemplo la Pizzería de
New York sería PizzeriaNewYork y la de Californía PizzeríaCalifornia que implementarán el
método con los ingredientes de sus zonas.
Las pizzas son diferentes según las zonas. No es igual la pizza de New York que la pizza de
California. Igualmente, aunque usarán los mismos ingredientes (tomate, mozzarella...) no los
obtendrán del mismo lugar, cada zona los comprará donde lo tenga más cerca. Así pues
podemos crear un método creador de Pizza que sea

Pizza(FactoriaIngredientes fi);

Como vemos utilizamos la factoría abstracta (no las concretas de cada zona, como podría ser
IngredientesNewYork o IngredientesCalifornia). Pizza podrá obtener los ingredientes de la
factoría independientemente de donde sea. Sería fácil crear nuevas factorías y añadirlas al
sistema para crear pizzas con estos nuevos ingredientes. Efectivamente, en este
ejemplo cliente es Pizza y es independiente de la Factoría usada.
El creador de la Pizza será el encargado de instanciar la factoría concreta, así pues los
encargados de instanciar las factorías concretas serán las pizzerías locales. En
PizzeríaNewYork podemos tener el método crearPizza() que realice el siguiente trabajo:

Pizza crearPizza() {
FactoríaIngredientes fi = new IngredientesNewYork();
Pizza pizza = new Pizza(fi); // Uso de la factoría
pizza.cortar();
pizza.empaquetar();
return pizza;
}

Como conclusión podemos observar que gracias a la factoría de ingredientes crear una nueva
zona, por ejemplo una pizzería en Barcelona, no nos implicaría estar modificando el código
existente, solo deberemos extenderlo (uno de los pilares de la Ingeniería del software) ya
crearíamos la subclase de Pizzería: PizzeríaBarcelona que al instanciar la factoría solo
debería escoger la factoría de Barcelona. Obviamente se debería crear la factoría de
Barcelona que se encargaría de crear los productos obtenidos de Barcelona. Así que en
ningún momento modificamos las pizzerías existentes, la superclase pizzería o las otras
factorías o productos, solo creamos nuevas clases.

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 nos resulta útil en escenarios donde es impreciso 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.

Estructura[editar]
En la imagen podemos ver la estructura del patrón:

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.
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);
}
}

Para el término matemático, véase Conjunto unitario.


Diagrama UML de una clase que implementa el patrón singleton.

En ingeniería de software, el patrón singleton (instancia única en inglés) es un patrón de


diseño diseñado para restringir la creación de objetos pertenecientes a una clase o el valor de
un tipo a un único objeto.
Su intención consiste en garantizar que una clase sólo tenga una instancia y proporcionar un
punto de acceso global a ella.
El patrón singleton se implementa creando en nuestra clase un método que crea una instancia
del objeto sólo si todavía no existe alguna. Para asegurar que la clase no puede ser
instanciada nuevamente se regula el alcance del constructor (con modificadores de
acceso como protegido o privado).
La instrumentación del patrón puede ser delicada en programas con múltiples hilos de
ejecución. Si dos hilos de ejecución intentan crear la instancia al mismo tiempo y esta no
existe todavía, sólo uno de ellos debe lograr crear el objeto. La solución clásica para este
problema es utilizar exclusión mutua en el método de creación de la clase que implementa el
patrón.
Las situaciones más habituales de aplicación de este patrón son aquellas en las que dicha
clase controla el acceso a un recurso físico único (como puede ser el ratón o un archivo
abierto en modo exclusivo) o cuando cierto tipo de datos debe estar disponible para todos los
demás objetos de la aplicación.
El patrón singleton provee una única instancia global gracias a que:

 La propia clase es responsable de crear la única instancia.


 Permite el acceso global a dicha instancia mediante un método de clase.
 Declara el constructor de clase como privado para que no sea instanciable directamente.
 Al estar internamente autoreferenciada, en lenguajes como Java, el recolector de basura
no actua.
 Una implementación correcta en el lenguaje de programación Java es la siguiente:

 public class Singleton {


 private static final Singleton INSTANCE = new Singleton();

 // El constructor privado no permite que se genere un
constructor por defecto.
 // (con mismo modificador de acceso que la definición de la
clase)
 private Singleton() {}

 public static Singleton getInstance() {
 return INSTANCE;
 }
 }

 Un ejemplo correcto de inicialización diferida. Se deja para comentar un error común


en Java al no tener en cuenta la sincronización de métodos.

 public class Singleton {


 private static Singleton INSTANCE = null;

 // Private constructor suppresses
 private Singleton(){}

 // creador sincronizado para protegerse de posibles problemas
multi-hilo
 // otra prueba para evitar instanciación múltiple
 private synchronized static void createInstance() {
 if (INSTANCE == null) {
 INSTANCE = new Singleton();
 }
 }

 public static Singleton getInstance() {
 if (INSTANCE == null) createInstance();
 return INSTANCE;
 }
 }

 Si queremos reducir el coste de la sincronización, se realiza la comprobación de la


instancia antes de invocar el método "createInstance"; también es posible hacerlo en
un único método de la siguiente manera [2]:

 private static void createInstance() {


 if (INSTANCE == null) {
 // Sólo se accede a la zona sincronizada
 // cuando la instancia no está creada
 synchronized(Singleton.class) {
 // En la zona sincronizada sería necesario volver
 // a comprobar que no se ha creado la instancia
 if (INSTANCE == null) {
 INSTANCE = new Singleton();
 }
 }
 }
 }

 Para asegurar que se cumpla el requerimiento de "única instancia" del singleton; la


clase debería producir un objeto no clonable:

 //Así se podría clonar el objeto y no tendría unicidad.


 SingletonObjectDemo clonedObject = (SingletonObjectDemo)
obj.clone();

 Entonces, se debería impedir la clonación sobreescribiendo el método "clone" de la


siguiente manera:

 //El método "clone" es sobreescrito por el siguiente que arroja una


excepción:
 public Object clone() throws CloneNotSupportedException {
 throw new CloneNotSupportedException();
 }

 Otra cuestión a tener en cuenta es que los métodos (o la clase) deberían ser
declarados como: final para que no puedan ser sobreescritos.

Estructural

Clase
Adapter (patrón de diseño)

El patrón Adapter (Adaptador) se utiliza para transformar una interfaz en otra, de tal modo que
una clase que no pudiera utilizar la primera, haga uso de ella a través de la segunda.

Propósito[editar]
Convierte la interfaz de una clase en otra interfaz que el cliente espera. Adapter permite a las
clases trabajar juntas, lo que de otra manera no podría hacerlo debido a sus interfaces
incompatibles.

También conocido como[editar]


 Wrapper (Envoltorio).

Aplicabilidad[editar]
usar el patrón Adapter cuando:

 Se desea usar una clase existente, y su interfaz no se iguala con la necesitada.


 Cuando se desea crear una clase reusable que coopera con clases no relacionadas, es
decir, las clases no tienen necesariamente interfaces compatibles.

Estructura[editar]

Participantes[editar]
 Target define la interfaz específica del dominio que Client usa.
 Client colabora con la conformación de objetos para la interfaz Target.
 Adaptee define una interfaz existente que necesita adaptarse
 Adapter adapta la interfaz de Adaptee a la interfaz Target

Colaboraciones[editar]
 Client llama a las operaciones sobre una instancia Adapter. De hecho, el adaptador llama
a las operaciones de Adaptee que llevan a cabo el pedido.

Consecuencias[editar]
los adaptadores de clase y objetos tienen diferentes trade-off

 Un adaptador de clase:
 Adapta Adaptee a Target encargando a una clase Adaptee concreta. Como
consecuencia, una clase adaptadora no funcionará cuando se desea adaptar una
clase y todas sus subclases.
 Permite a los Adapter sobrescribir algo de comportamiento de Adaptee, ya
que Adapter es una subclase de Adaptee.
 Un adaptador de objeto:
 Permite que un único Adapter trabaje con muchos Adaptees, es decir, el Adapter por
sí mismo y las subclases (si es que la tiene). El Adapter también puede agregar
funcionalidad a todos los Adaptees de una sola vez.
 Hace difícil sobrescribir el comportamiento de Adaptee. Esto requerirá
derivar Adaptee y hacer que Adapter se refiera a la subclase en lugar que
al Adaptee por sí mismo.
Aquí hay otras cuestiones a considerar cuando se utiliza el patrón Adapter:

 1.¿Cuanta adaptación hace el Adapter? Adapter varía en la cantidad de trabajo que hace
para adaptar Adaptee a la interfaz Target. Hay un espectro de trabajo posible, desde una
simple conversión (por ejemplo, cambiando los nombres de las operaciones) hasta
soportando un conjunto de operaciones enteramente diferentes. La cantidad de trabajo
que Adapter hace depende de cuanto de similar tienen la interfaz Target con Adaptee.
 2.Adaptadores Pluggables Una clase es más reusable cuando se minimiza la suposición
de que otras clases deben utilizarla. Mediante la construcción en una clase de la
adaptación de una interfaz, se elimina la suposición de que otras clases ven la misma
interfaz. Dicho de otra manera, la adaptación de la interfaz nos permite incorporar a
nuestra clase en sistemas existentes que pueden esperar diferentes interfaces de la
misma.

Implementación[editar]
Crear una nueva clase que será el Adaptador, que extienda del componente existente e
implemente la interfaz obligatoria. De este modo tenemos la funcionalidad que queríamos y
cumplimos la condición de implementar la interfaz.
La diferencia entre los patrones Adapter y Facade, es que el primero reutiliza una interfaz ya
existente, mientras que el segundo define una nueva con el objetivo de simplificarla.

package Structural_patterns;

public class AdapterWrapperPattern {

public static void main(String args[]){


Guitar eGuitar = new ElectricGuitar();
eGuitar.onGuitar();
eGuitar.offGuitar();
Guitar eAGuitar = new ElectricAcousticGuitar();
eAGuitar.onGuitar();
eAGuitar.offGuitar();
}
public abstract class Guitar{
abstract public void onGuitar();
abstract public void offGuitar();
}

public class ElectricGuitar extends Guitar{

public void onGuitar() {


System.out.println("Playing Guitar");
}

public void offGuitar() {


System.out.println("I'm tired to play the
guitar");
}
}

/**
* Class to Adapter/Wrapper
*/
public class AcousticGuitar{

public void play(){


System.out.println("Playing Guitar");
}
public void leaveGuitar(){
System.out.println("I'm tired to play the
guitar");
}
}

/**
* we Adapter/Wrapper AcousticGuitar into
* ElectricAcousticGuitar to adapt into the GuitarModel
*/
public class ElectricAcousticGuitar extends Guitar{
AcousticGuitar acoustic = new AcousticGuitar();
public void onGuitar() {
acoustic.play();
}

public void offGuitar() {


acoustic.leaveGuitar();
}
}
}

Objeto

El patrón Bridge, también conocido como Handle/Body, es una técnica usada en


programación para desacoplar una abstracción de su implementación, de manera que ambas
puedan ser modificadas independientemente sin necesidad de alterar por ello la otra.
Esto es, se desacopla una abstracción de su implementación para que puedan variar
independientemente.

Aplicabilidad[editar]
Se usa el patrón Bridge cuando:

 Se desea evitar un enlace permanente entre la abstracción y su implementación. Esto


puede ser debido a que la implementación debe ser seleccionada o cambiada en tiempo
de ejecución.
 Tanto las abstracciones como sus implementaciones deben ser extensibles por medio de
subclases. En este caso, el patrón Bridge permite combinar abstracciones e
implementaciones diferentes y extenderlas independientemente.
 Cambios en la implementación de una abstracción no deben impactar en los clientes, es
decir, su código no debe tener que ser recompilado.
 (En C++) Se desea esconder la implementación de una abstracción completamente a los
clientes. En C++, la representación de una clase es visible en la interface de la clase.
 Se desea compartir una implementación entre múltiples objetos (quizá usando
contadores), y este hecho debe ser escondido a los clientes.

Estructura[editar]
Participantes[editar]
 Abstraction define una interface abstracta. Mantiene una referencia a un objeto de
tipo Implementor.
 RefinedAbstraction extiende la interface definida por Abstraction
 Implementor define la interface para la implementación de clases. Esta interface no se
tiene que corresponder exactamente con la interface de Abstraction; de hecho, las dos
interfaces pueden ser bastante diferente. Típicamente la interface Implementor provee
sólo operaciones primitivas, y Abstraction define operaciones de alto nivel basadas en
estas primitivas.
 ConcreteImplementor implementa la interface de Implementor y define su
implementación concreta.

Colaboraciones[editar]
 Abstraction reenvía las peticiones de los clientes a su objeto Implementor.

Consecuencias[editar]
 1.Desacopla interfaz e implementación: una implementación no es limitada
permanentemente a una interface. La implementación de una abstracción puede ser
configurada en tiempo de ejecución. Además le es posible a un objeto cambiar su
implementación en tiempo de ejecución.
Desacoplando Abstraction e Implementor también elimina las dependencias sobre la
implementación en tiempo de compilación. Cambiar una clase de implementación no
require recompilar la clase Abstraction ni sus clientes. Esta propiedad es esencial cuando
te debes asegurar la compatibilidad binaria entre diferentes versiones de una biblioteca de
clases. Es más, este desacoplamiento fomenta las capas, que pueden conducir a un
sistema mejor estructurado. La parte de alto nivel de un sistema sólo tiene que
conocer Abstraction e Implementor.
 2.Mejora la extensibilidad: se puede extender las jerarquías
de Abstraction e Implementor independientemente.
 3.Esconde los detalles de la implementación a los clientes.
Implementación[editar]
Consideremos las siguientes cuestiones de implementación cuando se aplica este patrón:

 1.Sólo un Implementor: en situaciones donde existe sólo una implementación, crear una
clase Implementor abstracta no es necesario. Esto es un caso especial del patrón; hay
una relación uno-a-uno entre Abstraction e Implementor. Sin embargo, esta separación es
aún muy útil cuando un cambio en la implementación de una clase no debe afectar a sus
clientes existente, es decir, ellos no deben ser recompilados, sólo relinkeados. En C++, la
interface de la clase Implementor puede ser definida en un archivo header privado el cual
no es proveído a los clientes. Esto permite esconder una implementación de una clase
completamente de sus clientes.
 2 Creando el objeto Implementor adecuado: ¿Cómo, cuándo y dónde que clase
Implementor instanciar cuando hay más de una?Si Abstraction conoce todas las clases
ConcreteImplementor puede instanciar una de ellas en su constructor; puede decidir cuál
instanciar dependiendo de los parámetros del constructor.
Otra aproximación es elegir una implementación inicial por defecto y cambiarla después
acorde al uso. También es posible delegar la decisión a otro objeto en conjunto.

 3 Compartiendo implementadores: el estilo Handle/Body en C++ puede ser usado para


compartir implementaciones de muchos objetos. Body almacena una cuenta de referencia
que la clase Handle incrementa y decrementa.
 4 Usando herencia múltiple. Se puede usar herencia múltiple en C++ para asociar una
interfaz con su implementación.
Creamos una clase Abstracción padre que sea abstracta, además de abstracciones concretas
mediante clases que heredan de ella. Por otro lado se tienen las clases que implementan la
funcionalidad con una estructura similar: una clase ImplementaciónAbstracta padre, y todas
las clases hijas necesarias que implementan la funcionalidad de todas las maneras
necesarias. La relación se da entre la clase abstracta Abstracción y la clase abstracta
Implementación, delegando la primera la implementación en la segunda, que a su vez la
delega en las implementaciones concretas.

Código en java[editar]
interface Implementador {
public abstract void operacion();
}

/** primera implementacion de Implementador **/


class ImplementacionA implements Implementador{
public void operacion() {
System.out.println("Esta es la implementacion A");
}
}
/** segunda implementacion de Implementador **/
class ImplementacionB implements Implementador{
public void operacion() {
System.out.println("Esta es una implementacion de B");
}
}
/** interfaz de abstracción **/
interface Abstraccion {
public void operacion();
}
/** clase refinada que implementa la abstraccion **/
class AbstraccionRefinada implements Abstraccion{
private Implementador implementador;
public AbstraccionRefinada(Implementador implementador){
this.implementador = implementador;
}
public void operacion(){
implementador.operacion();
}
}
/** aplicacion que usa el patrón Bridge **/
public class EjemploBridge {
public static void main(String[] args) {
Abstraccion[] abstracciones = new Abstraccion[2];
abstracciones[0] = new AbstraccionRefinada(new
ImplementacionA());
abstracciones[1] = new AbstraccionRefinada(new
ImplementacionB());
for(Abstraccion abstraccion:abstracciones)
abstraccion.operacion();
}
}

Composite (patrón de diseño)


El patrón Composite sirve para construir objetos complejos a partir de otros más simples y
similares entre sí, gracias a la composición recursiva como. Un panal de abejas
Esto simplifica el tratamiento de los objetos creados, ya que al poseer todos ellos una interfaz
común, se tratan todos de la misma manera. Dependiendo de la implementación, pueden
aplicarse procedimientos al total o una de las partes de la estructura compuesta como si de un
nodo final se tratara, aunque dicha parte esté compuesta a su vez de muchas otras. Un claro
ejemplo de uso extendido de este patrón se da en los entornos de programación 2D para
aplicaciones gráficas. Un videojuego puede contener diferentes capas "layers" de sprites
(como una capa de enemigos) pudiéndose invocar un método que actúe sobre toda esta capa
de sprites a la vez (por ejemplo, para ocultarlos, darles un filtro de color etc.).

Índice
[ocultar]

 1Problema que soluciona


 2Implementación
 3Diagrama
 4Ejemplos de utilización
 5Código en C++
 6Código en Java
 7Código completo en C#

Problema que soluciona[editar]


Imaginemos que necesitamos crear una serie de clases para guardar información acerca de
una serie de figuras que serán círculos, cuadrados y triángulos. Además necesitamos poder
tratar también grupos de imágenes porque nuestro programa permite seleccionar varias de
estas figuras a la vez para moverlas por la pantalla.
En principio tenemos las clases Círculo, Cuadrado y Triángulo, que heredarán de una clase
padre que podríamos llamar Figura e implementarán todas la operación pintar(). En cuanto a
los grupos de Figuras podríamos caer en la tentación de crear una clase particular separada
de las anteriores llamada GrupoDeImágenes, también con un método pintar().
Problema.
Esta idea de separar en clases privadas componentes (figuras) y contenedores (grupos) tiene
el problema de que, para cada uno de las dos clases, el método pintar() tendrá una
implementación diferente, aumentando la complejidad del sistema.

Implementación[editar]
El patrón Composite da una solución elegante a este problema, de la que además resulta en
una implementación más sencilla.
A la clase Figura la llamaríamos Gráfico y de ella extenderían
tanto Círculo, Cuadrado y Triángulo, como GrupoDeImágenes. Además, esta última tendría
una relación todo-parte de multiplicidad * con Gráfico: un GrupoDeImágenes contendría
varios Gráficos, ya fuesen éstos Cuadrados, Triángulos, u otras clases GrupoDeImágenes.
Así, es posible definir a un grupo de imágenes recursivamente. Por ejemplo, un objeto cuya
clase es GrupoDeImágenes podría contener un Cuadrado, un Triángulo y
otro GrupoDeImágenes, este grupo de imágenes podría contener un Círculo y un Cuadrado.
Posteriormente, a este último grupo se le podría añadir otro GrupoDeImágenes, generando
una estructura de composición recursiva en árbol, por medio de muy poca codificación y un
diagrama sencillo y claro.

Diagrama[editar]
Ejemplos de utilización[editar]

Código en Java[editar]
import java.util.*;

public class Client {


public static void main(String[] args) {
Compuesto raiz = new Compuesto("root");
raiz.agregar(new Hoja("hoja A"));
raiz.agregar(new Hoja("hoja B"));

Compuesto comp = new Compuesto("compuesto X");


comp.agregar(new Hoja("hoja XA"));
comp.agregar(new Hoja("hoja XB"));
raiz.agregar(comp);
raiz.agregar(new Hoja("hoja C"));

Hoja l = new Hoja("hoja D");


raiz.agregar(l);
raiz.eliminar(l);
raiz.mostrar(1);
}
}

abstract class Componente {


protected String nombre;
public Componente (String nombre) {
this.nombre = nombre;
}
abstract public void agregar(Componente c);
abstract public void eliminar(Componente c);
abstract public void mostrar(int profundidad);
}

class Compuesto extends Componente {


private ArrayList<Componente> hijo = new ArrayList<Componente>();

public Compuesto (String name) {


super(name);
}

public void agregar(Componente componente) {


hijo.add(componente);
}

public void eliminar(Componente componente) {


hijo.remove(componente);
}

public void mostrar(int profundidad) {


System.out.println(nombre + " nivel: " + profundidad);
for (int i = 0; i < hijo.size(); i++)
hijo.get(i).mostrar(profundidad + 1);
}
}

class Hoja extends Componente {


public Hoja (String nombre) {
super(nombre);
}

public void agregar(Componente c) {


System.out.println("no se puede agregar la hoja");
}
public void eliminar(Componente c) {
System.out.println("no se puede quitar la hoja");
}

public void mostrar(int depth) {


System.out.println('-' + "" + nombre);
}
}

Decorator (patrón de diseño)


El patrón Decorator responde a la necesidad de añadir dinámicamente funcionalidad a un
Objeto. Esto nos permite no tener que crear sucesivas clases que hereden de la primera
incorporando la nueva funcionalidad, sino otras que la implementan y se asocian a la primera.

Motivación[editar]
Un ejemplo para poder ver la aplicabilidad del patrón decorador podría ser el siguiente:

 Disponemos de una herramienta para crear interfaces gráficas, que permite añadir
funcionalidades como bordes o barras de desplazamiento a cualquier componente de la
interfaz.
 Una posible solución sería utilizar la herencia para extender las responsabilidades de la
clase. Si optamos por esta solución, estaríamos haciendo un diseño inflexible (estático),
ya que el cliente no puede controlar cuándo y cómo decorar el componente con esa
propiedad.
 La solución está en encapsular dentro de otro objeto, llamado Decorador, las nuevas
responsabilidades. El decorador redirige las peticiones al componente y, además, puede
realizar acciones adicionales antes y después de la redirección. De este modo, se pueden
añadir decoradores con cualidades añadidas recursivamente.
 En este diagrama de clases, podemos ver que la interfaz decorador implementa la interfaz
del componente, redirigiendo todos los métodos al componente visual que encapsula.
 Las subclases decoradoras refinan los métodos del componente, añadiendo
responsabilidades.
 También se puede ver que el cliente no necesita hacer distinción entre los componentes
visuales decorados y los sin decorar.
Aplicabilidad[editar]
 Añadir responsabilidades a objetos individuales de forma dinámica y transparente
 Responsabilidades de un objeto pueden ser retiradas
 Cuando la extensión mediante la herencia no es viable
 Hay una necesidad de extender la funcionalidad de una clase, pero no hay razones para
extenderlo a través de la herencia.
 Existe la necesidad de extender dinámicamente la funcionalidad de un objeto y quizás
quitar la funcionalidad extendida.

Estructura[editar]
Participantes[editar]
 Componente
Define la interfaz para los objetos que pueden tener responsabilidades añadidas.

 Componente Concreto
Define un objeto al cual se le pueden agregar responsabilidades adicionales.

 Decorador
Mantiene una referencia al componente asociado. Implementa la interfaz de la superclase
Componente delegando en el componente asociado.

 Decorador Concreto
Añade responsabilidades al componente.

Colaboraciones[editar]
 El decorador redirige las peticiones al componente asociado.
 Opcionalmente puede realizar tareas adicionales antes y después de redirigir la petición.

Consecuencias[editar]
 Más flexible que la herencia. Al utilizar este patrón, se pueden añadir y eliminar
responsabilidades en tiempo de ejecución. Además, evita la utilización de la herencia con
muchas clases y también, en algunos casos, la herencia múltiple.
 Evita la aparición de clases con muchas responsabilidades en las clases superiores de la
jerarquía. Este patrón nos permite ir incorporando de manera incremental
responsabilidades.
 Genera gran cantidad de objetos pequeños. El uso de decoradores da como resultado
sistemas formados por muchos objetos pequeños y parecidos.
 Puede haber problemas con la identidad de los objetos. Un decorador se comporta como
un envoltorio transparente. Pero desde el punto de vista de la identidad de objetos, estos
no son idénticos, por lo tanto no deberíamos apoyarnos en la identidad cuando estamos
usando decoradores.

Implementación[editar]
El patrón Decorator soluciona este problema de una manera mucho más sencilla y extensible.
Se crea a partir de Ventana la subclase abstracta VentanaDecorator y, heredando de
ella, BordeDecorator y BotonDeAyudaDecorator. VentanaDecorator encapsula el
comportamiento de Ventana y utiliza composición recursiva para que sea posible añadir tantas
"capas" de Decorators como se desee. Podemos crear tantos Decorators como queramos
heredando de VentanaDecorator.

Ejemplo Java[editar]

public abstract class Componente{


abstract public void operacion();
}

public class ComponenteConcreto extends Componente{


public void operacion(){
System.out.println("ComponenteConcreto.operacion()");
}
}

public abstract class Decorador extends Componente{


private Componente _componente;

public Decorador(Componente componente){


_componente = componente;
}

public void operacion(){


_componente.operacion();
}
}
public class DecoradorConcretoA extends Decorador{
private String _propiedadAñadida;

public DecoradorConcretoA(Componente componente){


super(componente);
}

public void operacion(){


super.operacion();
_propiedadAñadida = "Nueva propiedad";
System.out.println("DecoradorConcretoA.operacion()");
}
}

public class DecoradorConcretoB extends Decorador{


public DecoradorConcretoB(Componente componente){
super(componente);
}

public void operacion(){


super.operacion();
comportamientoAñadido();
System.out.println("DecoradorConcretoB.operacion()");
}

public void comportamientoAñadido(){


System.out.println("Comportamiento B añadido");
}
}

public class Cliente{


public static void main(String[] args){
ComponenteConcreto c = new ComponenteConcreto();
DecoradorConcretoA d1 = new DecoradorConcretoA(c);
DecoradorConcretoB d2 = new DecoradorConcretoB(d1);
d2.operacion();
}
}
Fachada (Facade) es un tipo de patrón de diseño estructural. Viene motivado por la necesidad
de estructurar un entorno de programación y reducir su complejidad con la división en
subsistemas, minimizando las comunicaciones y dependencias entre estos.

Consideraciones para su aplicación[editar]


Se aplicará el patrón fachada cuando se necesite proporcionar una interfaz simple para un
subsistema complejo, o cuando se quiera estructurar varios subsistemas en capas, ya que las
fachadas serían el punto de entrada a cada nivel. Otro escenario proclive para su aplicación
surge de la necesidad de desacoplar un sistema de sus clientes y de otros subsistemas,
haciéndolo más independiente, portable y reutilizable (esto es, reduciendo dependencias entre
los subsistemas y los clientes).

Estructura[editar]
Se puede ver en la siguiente figura:

A continuación, se muestra un ejemplo:


Participantes[editar]
Fachada (Facade): conoce qué clases del subsistema son responsables de una determinada
petición, y delega esas peticiones de los clientes a los objetos apropiados del subsistema.
Subclases (ModuleA, ModuleB, ModuleC...): implementan la funcionalidad del subsistema.
Realizan el trabajo solicitado por la fachada. No conocen la existencia de la fachada.

Colaboraciones[editar]
Los clientes que se comunican con el subsistema enviando peticiones al objeto Fachada, el
cual las reenvía a los objetos apropiados del subsistema.
Los objetos del subsistema realizan el trabajo final, y la fachada hace algo de trabajo para
pasar de su interfaz a las del subsistema.
Los clientes que usan la fachada no tienen que acceder directamente a los objetos del
subsistema.

Ventajas e inconvenientes[editar]
La principal ventaja del patrón fachada consiste en que para modificar las clases de los
subsistemas, sólo hay que realizar cambios en la interfaz/fachada, y los clientes pueden
permanecer ajenos a ello. Además, y como se mencionó anteriormente, los clientes no
necesitan conocer las clases que hay tras dicha interfaz.
Como inconveniente, si se considera el caso de que varios clientes necesiten acceder a
subconjuntos diferentes de la funcionalidad que provee el sistema, podrían acabar usando
sólo una pequeña parte de la fachada, por lo que sería conveniente utilizar varias fachadas
más específicas en lugar de una única global.
Patrones relacionados[editar]
Uno de los patrones relacionados más directamente es el singleton, dado que en
determinadas ocasiones las fachadas pueden ser instancias únicas.
Otros patrones que guardan una cierta relación con el patrón fachada son
los GRASP (General Responsibility Assignment Software Patterns), los cuales no son
patrones de diseño, sino buenas prácticas que guían al desarrollador para encontrar los
patrones de diseño, que son más concretos. Uno de los patrones GRASP es un controlador
que actúa como punto de entrada en la capa lógica, lo que se puede comparar perfectamente
con el uso del patrón fachada.

Usos conocidos (Problemas/Soluciones)[editar]


Problema: Un cliente necesita acceder a parte de la funcionalidad de un sistema más
complejo.

 Definir una interfaz que permita acceder solamente a esa funcionalidad.


Problema: Existen grupos de tareas muy frecuentes para las que se puede crear código
más sencillo y legible.

 Definir funcionalidad que agrupe estas tareas en funciones o métodos sencillos y


claros.
Problema: Una biblioteca es difícilmente legible.

 Crear un intermediario más legible.


Problema: Dependencia entre el código del cliente y la parte interna de una
biblioteca.

 Crear un intermediario y realizar llamadas a la biblioteca sólo o, sobre todo, a


través de él.
Problema: Necesidad de acceder a un conjunto de APIs que pueden además
tener un diseño no muy bueno.

 Crear una API intermedia, bien diseñada, que permita acceder a la funcionalidad
de las demás.
Problema: Muchas clases cliente quieren usar varias clases servidoras, y
deben saber cuál es exactamente la que le proporciona cada servicio. El
sistema se volvería muy complejo, porque habría que relacionar todas las
clases cliente con todas y cada una de las clases servidoras.

 Crear una o varias clases Facade, que implementen todos los servicios, de modo
que o todos los clientes utilicen esa única clase, o que cada grupo de clientes use
la fachada que mejor se ajuste a sus necesidades.

Ejemplos de utilización[editar]
En Java las clases java.awt.Graphics y java.awt.Font.
En el siguiente ejemplo implementado en java, se puede visualizar
como se resuelve el problema de hacer mas legible y no repetir
código para las tareas mas frecuentes.

Implementación (Java)[editar]
package com.genbetadev;

public class Impresora {

private String tipoDocumento;

private String hoja;

private boolean color;

private String texto;

public String getTipoDocumento() {

return tipoDocumento;

public void setTipoDocumento(String


tipoDocumento) {

this.tipoDocumento =
tipoDocumento;

public void setHoja(String hoja) {


this.hoja = hoja;
}

public String getHoja() {

return hoja;

}
public void setColor(boolean color) {
this.color = color;
}

public boolean getColor() {

return color;

public void setTexto(String texto) {


this.texto = texto;
}

public String getTexto() {

return texto;

public void imprimir() {

impresora.imprimirDocumento();

}
}

Se trata de una clase sencilla que imprime documentos en uno u otro


formato. El código de la clase cliente nos ayudará a entender mejor
su funcionamiento.

package com.genbetadev;

public class PrincipalCliente {

public static void main(String[] args) {


Impresora i = new
Impresora();

i.setHoja("a4");

i.setColor(true);

i.setTipoDocumento("pdf");

i.setTexto("texto 1");

i.imprimirDocumento();

Impresora i2 = new
Impresora();

i2.setHoja("a4");

i2.setColor(true);

i2.setTipoDocumento("pdf");

i2.setTexto("texto 2");

i2.imprimirDocumento();

Impresora i3 = new
Impresora();

i3.setHoja("a3");

i3.setColor(false);

i3.setTipoDocumento("excel");

i3.setTexto("texto 3");

i3.imprimirDocumento();
}

Como podemos ver la clase cliente se encarga de invocar a la


impresora, y configurarla para después imprimir varios documentos
.Ahora bien prácticamente todos los documentos que escribimos
tienen la misma estructura (formato A4, Color , PDF). Estamos
continuamente repitiendo código. Vamos a construir una nueva clase
FachadaImpresoraNormal que simplifique la impresión de
documentos que sean los más habituales.

package com.genbetadev;

public class FachadaImpresoraNormal {

Impresora impresora;

public FachadaImpresoraNormal(String texto)


{

super();

impresora= new Impresora();

impresora.setColor(true);

impresora.setHoja("A4");

impresora.setTipoDocumento("PDF");

impresora.setTexto(texto);

public void imprimir() {


impresora.imprimirDocumento();

De esta forma el cliente quedará mucho más sencillo :

package com.genbetadev;

public class PrincipalCliente2 {

public static void main(String[] args) {

FachadaImpresoraNormal
fachada1= new FachadaImpresoraNormal("texto1");

fachada1.imprimir();

FachadaImpresoraNormal
fachada2= new FachadaImpresoraNormal("texto2");

fachada2.imprimir();

Impresora i3 = new
Impresora();

i3.setHoja("a4");

i3.setColor(true);

i3.setTipoDocumento("excel");

i3.setTexto("texto 3");

i3.imprimirDocumento();

}
}

Proxy (patrón de diseño)


El patrón Proxy es un patrón estructural que tiene como propósito proporcionar
un subrogado o intermediario de un objeto para controlar su acceso.

Patrón Proxy

Índice
[ocultar]

 1Motivación
 2Aplicabilidad
 3Participantes
 4Colaboraciones.
 5Consecuencias.
 6Implementación.
o 6.1Ejemplo Java
 7Patrones relacionados.
 8Ejemplos comunes de la aplicación del patrón proxy.

Motivación[editar]
Para explicar la motivación del uso de este patrón veamos un escenario donde su aplicación
sería la solución más adecuada al problema planteado. Consideremos un editor que puede
incluir objetos gráficos dentro de un documento. Se requiere que la apertura de un documento
sea rápida, mientras que la creación de algunos objetos (imágenes de gran tamaño) es cara.
En este caso no es necesario crear todos los objetos con imágenes nada más abrir el
documento porque no todos los objetos son visibles. Interesa por tanto retrasar el coste de
crear e inicializar un objeto hasta que es realmente necesario (por ejemplo, no abrir las
imágenes de un documento hasta que no son visibles). La solución que se plantea para ello
es la de cargar las imágenes bajo demanda. Pero, ¿cómo cargar las imágenes bajo demanda
sin complicar el resto del editor? La respuesta es utilizar un objeto proxy. Dicho objeto se
comporta como una imagen normal y es el responsable de cargar la imagen bajo demanda.

Aplicabilidad[editar]
El patrón proxy se usa cuando se necesita una referencia a un objeto más flexible o
sofisticada que un puntero. Dependiendo de la función que se desea realizar con dicha
referencia podemos distinguir diferentes tipos de proxies:

 proxy remoto: representante local de un objeto remoto.


 proxy virtual: crea objetos costosos bajo demanda (como la clase ImagenProxy en el
ejemplo de motivación).
 proxy de protección: controla el acceso al objeto original (ejemplo de proxy de
protección: [1])
 proxy de referencia inteligente: sustituto de un puntero que lleva a cabo operaciones
adicionales cuando se accede a un objeto (ej. contar número de referencias al objeto real,
cargar un objeto persistente bajo demanda en memoria, control de concurrencia de
acceso tal como bloquear el objeto para impedir acceso concurrente, …).

Participantes[editar]
La clase Proxy : mantiene una referencia al objeto real (en el siguiente ejemplo se le
denomina _sujetoReal) y proporciona una interfaz idéntica al sujeto (la clase Sujeto).
Además controla el acceso a dicho objeto real y puede ser el responsable de su creación y
borrado. También tiene otras responsabilidades que dependen del tipo de proxy:

 proxy remoto: responsable de codificar una petición y sus argumentos, y de enviarla al


objeto remoto.
 proxy virtual: puede hacer caché de información del objeto real para diferir en lo posible
el acceso a este.
 proxy de protección: comprueba que el cliente tiene los permisos necesarios para
realizar la petición.

La clase Sujeto: define una interfaz común para el proxy (Proxy) y el objeto real (de la
clase SujetoReal), de tal modo que se puedan usar de manera indistinta.

La clase SujetoReal: clase del objeto real que el proxy representa.

Colaboraciones.[editar]
Dependiendo de la clase de proxy, el objeto proxy redirige las peticiones al objeto real que
representa.

Ejemplos de funcionamiento:

 Diagrama de clases para un ejemplo del patrón proxy.[2]


 Diagrama de secuencia para un ejemplo en el que no se utiliza el patrón proxy. [3]
 Diagrama de secuencia para un ejemplo en el que se utiliza el patrón proxy.[4]

Consecuencias.[editar]
El uso de un proxy introduce un nivel de indirección adicional con diferentes usos:
 Un proxy remoto oculta el hecho de que un objeto reside en otro espacio de direcciones.
 Un proxy virtual puede realizar optimizaciones, como la creación de objetos bajo
demanda.
 El proxy de protección y las referencias inteligentes permiten realizar diversas tareas
de mantenimiento adicionales al acceder a un objeto.
Además, su uso también permite realizar una optimización COW (copy-on-write) , puesto
que copiar un objeto grande puede ser costoso, y si la copia no se modifica, no es necesario
incurrir en dicho gasto. Además el sujeto mantiene un número de referencias, y sólo cuando
se realiza una operación que modifica el objeto, éste se copia. Es útil por tanto para retrasar la
replicación de un objeto hasta que cambia.

Implementación.[editar]
Ejemplo Java[editar]

/**
* Clase Cliente: el cliente del sujeto tan solo conoce que maneja un
objeto de la
* clase Sujeto. Por tanto, funciona indistintamente con el SujetoReal
* como con su Proxy.
*/

public class Cliente {

/**
* El constructor guarda la referencia al sujeto.
*/

public Cliente(Sujeto sujeto) {


_sujeto = sujeto;
}
/**
* Lo único que realiza el cliente en su método ejecutar es
* llamar 2 veces al metodo1, luego 1 vez al metodo2,
* y de nuevo al metodo1. _sujeto, ejecutará el método dependiendo
* si fue creado como proxy o sujetoReal.
*/

public void ejecutar() {


_sujeto.metodo1();
_sujeto.metodo1();
_sujeto.metodo2();
_sujeto.metodo1();
}
/**
* La clase Cliente tiene el atributo _sujeto que le permite tener una
referencia al sujeto al que
* el cliente envía la petición de ejecutar un determinado método.
*/

private Sujeto _sujeto;

}
/**
* La clase Sujeto dentro del patrón Proxy es la interfaz del sujeto
* real de cara al exterior (Cliente). Es una clase abstracta cuyos
* métodos serán implementados tanto por el sujeto real como por el
proxy.
*/

public abstract class Sujeto {

/**
* El constructor guarda el nombre del sujeto.
*/
public Sujeto(String nombre) {
_nombre = nombre;
}

/**
* Método que devuelve el nombre del sujeto.
*/
public String toString() {
return _nombre;
}

/**
* Métodos definidos de forma abstracta en la clase Sujeto, y que
tendrán distintas implementaciones en las clases que heredan de ésta:
Proxy
* y SujetoReal.
*/
public abstract void metodo1();
public abstract void metodo2();

/**
* Este método llama al método toString() de la clase Proxy. Se le pasa
un objeto de la clase Sujeto, pero se considera que se trata de un objeto
proxy.
*/

public void status (Sujeto sujeto) {


Proxy p;
p = (Proxy) sujeto;
p.toString();
}

/**
* La clase Sujeto tiene el atributo _nombre , que indica el nombre
de un sujeto, tanto si se trata de un proxy
* como de un sujeto real.
*/

private String _nombre;

}
/**
* Éste es el objeto Proxy. Este proxy es simultáneamente un
*
* (a) proxy virtual que retrasa la creación del objeto real hasta que
* se invoca alguno de sus métodos.
* (b) referencia inteligente, realizando labores de contabilización
* del número de veces que se invoca un método.
*/

public class Proxy extends Sujeto {

/**
* el constructor de la clase, además de inicializar a la parte
* correspondiente a la superclase, establece a null la referencia
* al sujeto real e inicializa la contabilización.
*/
public Proxy (String nombre) {
super(nombre);
_sujetoReal = null;
_accesosMetodo1 = 0;
_accesosMetodo2 = 0;
}

/**
* En lugar de realizar de cada vez una comprobación de si el
* sujeto real esta creado y en caso contrario crearlo, se define
* este método privado.
*/

private SujetoReal obtenerSujetoReal() {


if (_sujetoReal == null)
_sujetoReal = new SujetoReal(this + " (Real)");

return _sujetoReal;
}
/**
* Los métodos delegan en el sujeto real.
*/

public void metodo1() {


_accesosMetodo1++;
obtenerSujetoReal().metodo1();
}

public void metodo2() {


_accesosMetodo2++;
obtenerSujetoReal().metodo2();
}

/**
* Este método permite presentar información de contabilización
* de uso del objeto.
*/

public String toString() {


if (_sujetoReal != null)
System.out.println("Accesos a " + _sujetoReal +
": metodo1=" + _accesosMetodo1 +
", metodo2=" + _accesosMetodo2);
else
System.out.println("Sujeto Real (" + this + ") no creado.");

return "";
}

/**
* Atributos privados: _sujetoReal que le permite a la clase Proxy
tener una referencia al sujeto real y los contabilizadores de los accesos
a
* los métodos 1 y 2.
*/

private SujetoReal _sujetoReal;


private int _accesosMetodo1, _accesosMetodo2;
}
/**
* La clase SujetoReal es el objeto sobre el que queremos
* implementar un proxy. Extiende la clase Sujeto implementando los
* métodos del sujeto (en realidad es el sujeto el que presenta
* la interfaz de los métodos del sujeto real...)
*/

public class SujetoReal extends Sujeto {

public SujetoReal(String nombre) {


super(nombre);
// aquí aparece el código de una inicialización costosa: por
ejemplo, añadir un objeto (que se le pasase como parámetro al
constructor) a
// un vector que tuviese como atributo esta misma clase
(SujetoReal), y luego ordenar dicho vector de
// mayor a menor en función de un atributo entero que tuviese la
clase a la que pertenecen los objetos que contiene el vector.
// Otro ejemplo de inicialización costosa sería el llamar en el
constructor a un método de esta clase: por
// ejemplo loadImageFromDisk() lo cúal sería lógico si se tratase
de una clase ImagenReal que tuviese como proxy la clase ProxyReal y
// como clase abstracta de la que hereda, la clase Imagen.
}

/**
* Se implementan los dos métodos abstractos del sujeto.
*/

public void metodo1() {


System.out.println("Ejecutando metodo1 en " + this);
}

public void metodo2() {


System.out.println("Ejecutando metodo2 en " + this);
}

}
/**
* Programa principal.
*/

public class Main {

public static void main(String argv[]) {

Sujeto objetoA = new Proxy("objetoA");


Cliente c = new Cliente(objetoA);
objetoA.status(objetoA);
c.ejecutar();
objetoA.status(objetoA);

Sujeto objetoB = new SujetoReal("objetoB");


Cliente d = new Cliente(objetoB);

d.ejecutar();
}

}
Patrones relacionados.[editar]
 El patrón Adaptador proporciona una interfaz diferente al objeto que adapta, mientras que
el proxy tiene la misma interfaz, pero ambos redirigen la petición del cliente al verdadero
sujeto que la ejecuta con la posibilidad de incorporar lógica adicional : comprobación de
acceso, creación del sujeto real…

 El Proxy se puede diseñar de manera similar al patrón decorador, pero el propósito es


diferente: el decorador añade responsabilidades a un objeto, el proxy sólo controla su
acceso. Así, si el proxy no tiene una fuerte dependencia con el sujeto real (por ejemplo, no
es de creación), y no tiene que instanciarlo, puede adoptar el mismo diseño que el
decorador, y ser un proxy de cualquier sujeto (referencia a la interfaz que el cliente
conoce).

Ejemplos comunes de la aplicación del patrón proxy.[editar]


A continuación se presentan algunos de los ejemplos más comunes en los que se utiliza el
patrón proxy :

 Añadir acceso de seguridad a un objeto existente. El proxy determinará si el cliente puede


acceder al objeto de interés (proxy de protección).
 Proporcionando interfaz de recursos remotos como el servicio web o recursos REST.
 Coordinación de las operaciones costosas en recursos remotos pidiendo los recursos a
distancia para iniciar la operación tan pronto como sea posible antes de acceder a los
recursos.
 Agregar una operación segura para los subprocesos a una clase existente sin cambiar el
código de la clase existente.

Das könnte Ihnen auch gefallen