Beruflich Dokumente
Kultur Dokumente
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 uso:
Abstract Factory
(Redirigido desde «Abstract Factory (patrón de diseño)»)
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]
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.
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;
}
}
function writeln($line_in) {
echo $line_in."<br/>";
}
?>
using System;
// "Prototipo"
abstract class Prototipo {
public string ID {
get{ return _id; }
}
class Prototipo[[Client]] {
ClaseConcreta1 p1 = new ClaseConcreta("Clone-I");
ClaseConcreta1 c1 = (ClaseConcreta1) p1.Clone();
Console.WriteLine( "Clonación: {0}", c1.ID );
}
}
public FactoriaPrototipo() {
mapaObjetos = new HashMap();
// Se incluyen en el mapa todos los productos prototipo
mapaObjetos.put("producto 1", new UnProducto(1));
}
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.
Aplicabilidad[editar]
usar el patrón Adapter cuando:
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;
/**
* Class to Adapter/Wrapper
*/
public class AcousticGuitar{
/**
* 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();
}
Objeto
Aplicabilidad[editar]
Se usa el patrón Bridge cuando:
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.
Código en java[editar]
interface Implementador {
public abstract void operacion();
}
Índice
[ocultar]
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.*;
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]
Estructura[editar]
Se puede ver en la siguiente figura:
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.
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;
return tipoDocumento;
this.tipoDocumento =
tipoDocumento;
return hoja;
}
public void setColor(boolean color) {
this.color = color;
}
return color;
return texto;
impresora.imprimirDocumento();
}
}
package com.genbetadev;
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();
}
package com.genbetadev;
Impresora impresora;
super();
impresora.setColor(true);
impresora.setHoja("A4");
impresora.setTipoDocumento("PDF");
impresora.setTexto(texto);
package com.genbetadev;
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();
}
}
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:
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:
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.
Colaboraciones.[editar]
Dependiendo de la clase de proxy, el objeto proxy redirige las peticiones al objeto real que
representa.
Ejemplos de funcionamiento:
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.
*/
/**
* El constructor guarda la referencia al 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.
*/
/**
* 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.
*/
/**
* 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.
*/
}
/**
* É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.
*/
/**
* 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.
*/
return _sujetoReal;
}
/**
* Los métodos delegan en el sujeto real.
*/
/**
* Este método permite presentar información de contabilización
* de uso del objeto.
*/
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.
*/
/**
* Se implementan los dos métodos abstractos del sujeto.
*/
}
/**
* Programa principal.
*/
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…