Hoy vamos a analizar el patrón de diseño Flyweight.
Patrón de diseño Flyweight
Según GoF, la intención del patrón de diseño flyweight es:
Utilizar el uso compartido para respaldar eficientemente un gran número de objetos finos
El patrón de diseño Flyweight es un patrón de diseño estructural como el patrón de fachada, el patrón de adaptador y el patrón decorador. Se utiliza cuando necesitamos crear muchos objetos de una clase. Dado que cada objeto consume espacio de memoria que puede ser crucial para dispositivos con poca memoria, como dispositivos móviles o sistemas integrados, el patrón de diseño flyweight se puede aplicar para reducir la carga en la memoria compartiendo objetos. Antes de aplicar el patrón de diseño flyweight, debemos tener en cuenta los siguientes factores:
- El número de objetos a crear por la aplicación debe ser enorme.
- La creación de objetos es intensiva en memoria y también puede llevar tiempo.
- Las propiedades del objeto pueden dividirse en propiedades intrínsecas y extrínsecas, las propiedades extrínsecas de un objeto deben ser definidas por el programa cliente.
Para aplicar el patrón de peso ligero, necesitamos dividir las propiedades del objeto en propiedades intrínsecas y extrínsecas. Las propiedades intrínsecas hacen que el objeto sea único mientras que las propiedades extrínsecas son establecidas por el código del cliente y se utilizan para realizar diferentes operaciones. Por ejemplo, un objeto Círculo puede tener propiedades extrínsecas como color y ancho. Para aplicar el patrón de peso ligero, necesitamos crear una fábrica de peso ligero que devuelva los objetos compartidos. Para nuestro ejemplo, supongamos que necesitamos crear un dibujo con líneas y óvalos. Entonces tendremos una interfaz Forma
y sus implementaciones concretas como Línea
y Óvalo
. La clase Óvalo tendrá una propiedad intrínseca para determinar si rellenar el óvalo con el color dado o no, mientras que la Línea no tendrá ninguna propiedad intrínseca.
Interfaz y Clases Concretas del Patrón de Diseño de Peso Ligero
Forma.java
package com.journaldev.design.flyweight;
import java.awt.Color;
import java.awt.Graphics;
public interface Shape {
public void draw(Graphics g, int x, int y, int width, int height,
Color color);
}
Línea.java
package com.journaldev.design.flyweight;
import java.awt.Color;
import java.awt.Graphics;
public class Line implements Shape {
public Line(){
System.out.println("Creating Line object");
//agregando retardo de tiempo
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void draw(Graphics line, int x1, int y1, int x2, int y2,
Color color) {
line.setColor(color);
line.drawLine(x1, y1, x2, y2);
}
}
Óvalo.java
package com.journaldev.design.flyweight;
import java.awt.Color;
import java.awt.Graphics;
public class Oval implements Shape {
//propiedad intrínseca
private boolean fill;
public Oval(boolean f){
this.fill=f;
System.out.println("Creating Oval object with fill="+f);
//agregando retardo de tiempo
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void draw(Graphics circle, int x, int y, int width, int height,
Color color) {
circle.setColor(color);
circle.drawOval(x, y, width, height);
if(fill){
circle.fillOval(x, y, width, height);
}
}
}
Observa que he introducido intencionalmente un retraso en la creación del objeto de las clases concretas para resaltar que el patrón de peso ligero se puede utilizar para objetos que tardan mucho en instanciarse.
Fábrica de Peso Ligero
La fábrica de peso ligero será utilizada por programas clientes para instanciar el objeto, por lo que necesitamos mantener un mapa de objetos en la fábrica que no debe ser accesible por la aplicación cliente. Cada vez que el programa cliente llama para obtener una instancia del objeto, debería devolverse desde el HashMap; si no se encuentra, entonces se crea un nuevo objeto, se coloca en el mapa y luego se devuelve. Debemos asegurarnos de que se consideren todas las propiedades intrínsecas al crear el objeto. Nuestra clase de fábrica de peso ligero se ve como el siguiente código. ShapeFactory.java
package com.journaldev.design.flyweight;
import java.util.HashMap;
public class ShapeFactory {
private static final HashMap<ShapeType,Shape> shapes = new HashMap<ShapeType,Shape>();
public static Shape getShape(ShapeType type) {
Shape shapeImpl = shapes.get(type);
if (shapeImpl == null) {
if (type.equals(ShapeType.OVAL_FILL)) {
shapeImpl = new Oval(true);
} else if (type.equals(ShapeType.OVAL_NOFILL)) {
shapeImpl = new Oval(false);
} else if (type.equals(ShapeType.LINE)) {
shapeImpl = new Line();
}
shapes.put(type, shapeImpl);
}
return shapeImpl;
}
public static enum ShapeType{
OVAL_FILL,OVAL_NOFILL,LINE;
}
}
Observa el uso de Java Enum para seguridad de tipo, Java Composition (mapa de formas) y patrón de fábrica en el método getShape
.
Ejemplo de Cliente de Patrón de Diseño de Peso Ligero
A continuación se muestra un programa de muestra que utiliza la implementación del patrón de peso ligero. DrawingClient.java
package com.journaldev.design.flyweight;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.journaldev.design.flyweight.ShapeFactory.ShapeType;
public class DrawingClient extends JFrame{
private static final long serialVersionUID = -1350200437285282550L;
private final int WIDTH;
private final int HEIGHT;
private static final ShapeType shapes[] = { ShapeType.LINE, ShapeType.OVAL_FILL,ShapeType.OVAL_NOFILL };
private static final Color colors[] = { Color.RED, Color.GREEN, Color.YELLOW };
public DrawingClient(int width, int height){
this.WIDTH=width;
this.HEIGHT=height;
Container contentPane = getContentPane();
JButton startButton = new JButton("Draw");
final JPanel panel = new JPanel();
contentPane.add(panel, BorderLayout.CENTER);
contentPane.add(startButton, BorderLayout.SOUTH);
setSize(WIDTH, HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
Graphics g = panel.getGraphics();
for (int i = 0; i < 20; ++i) {
Shape shape = ShapeFactory.getShape(getRandomShape());
shape.draw(g, getRandomX(), getRandomY(), getRandomWidth(),
getRandomHeight(), getRandomColor());
}
}
});
}
private ShapeType getRandomShape() {
return shapes[(int) (Math.random() * shapes.length)];
}
private int getRandomX() {
return (int) (Math.random() * WIDTH);
}
private int getRandomY() {
return (int) (Math.random() * HEIGHT);
}
private int getRandomWidth() {
return (int) (Math.random() * (WIDTH / 10));
}
private int getRandomHeight() {
return (int) (Math.random() * (HEIGHT / 10));
}
private Color getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}
public static void main(String[] args) {
DrawingClient drawing = new DrawingClient(500,600);
}
}
I have used random number generation to generate different type of Shapes in our frame. If you run above client program, you will notice the delay in creating first Line Object and Oval objects with fill as true and false. After that the program executes quickly since its using the shared objects. After clicking “Draw” button multiple times, the frame looks like below image. And you will see following output in command line confirming that Objects are shared.
Creating Line object
Creating Oval object with fill=true
Creating Oval object with fill=false
Esto es todo para el patrón de peso ligero; exploraremos más patrones de diseño en futuras publicaciones. Si te gustó, comparte tus pensamientos en la sección de comentarios y compártelo con otros también.
Ejemplo de Patrón de Diseño de Peso Ligero en JDK
Todas las clases envolventes del método valueOf()
utilizan objetos en caché, mostrando el uso del patrón de diseño de peso ligero. El mejor ejemplo es la clase Java String con la implementación del String Pool.
Puntos Importantes del Patrón de Diseño Flyweight
- En nuestro ejemplo, el código del cliente no está obligado a crear objetos usando la fábrica Flyweight, pero podemos forzarlo para asegurarnos de que el código del cliente utilice la implementación del patrón flyweight, aunque es una decisión de diseño completa para una aplicación particular.
- El patrón Flyweight introduce complejidad y si el número de objetos compartidos es grande, entonces hay un intercambio entre memoria y tiempo, por lo que necesitamos usarlo con prudencia según nuestros requisitos.
- La implementación del patrón Flyweight no es útil cuando el número de propiedades intrínsecas del objeto es grande, lo que hace que la implementación de la clase Factory sea compleja.
Eso es todo para el patrón de diseño Flyweight en Java.
Source:
https://www.digitalocean.com/community/tutorials/flyweight-design-pattern-java