Oggi esamineremo il design pattern Flyweight.
Design Pattern Flyweight
Secondo GoF, l’intento del design pattern flyweight è:
Utilizzare la condivisione per supportare efficientemente un grande numero di oggetti dettagliati
Il design pattern Flyweight è un design pattern strutturale simile al pattern Facade, al pattern Adapter e al pattern Decorator. Il design pattern Flyweight viene utilizzato quando è necessario creare molti oggetti di una classe. Poiché ogni oggetto consuma spazio di memoria che può essere cruciale per dispositivi a bassa memoria, come dispositivi mobili o sistemi embedded, il design pattern Flyweight può essere applicato per ridurre il carico sulla memoria condividendo gli oggetti. Prima di applicare il design pattern Flyweight, è necessario considerare i seguenti fattori:
- Il numero di oggetti da creare nell’applicazione dovrebbe essere elevato.
- La creazione dell’oggetto è onerosa in termini di memoria e può richiedere tempo.
- Le proprietà dell’oggetto possono essere suddivise in proprietà intrinseche ed estrinseche, le proprietà estrinseche di un oggetto dovrebbero essere definite dal programma client.
Per applicare il pattern flyweight, dobbiamo suddividere la proprietà dell’oggetto in proprietà intrinseche ed estrinseche. Le proprietà intrinseche rendono l’oggetto unico, mentre le proprietà estrinseche vengono impostate dal codice client e utilizzate per eseguire diverse operazioni. Ad esempio, un oggetto Cerchio può avere proprietà estrinseche come colore e larghezza. Per applicare il pattern flyweight, dobbiamo creare una fabbrica di flyweight che restituisce gli oggetti condivisi. Nel nostro esempio, supponiamo di dover creare un disegno con linee e ovali. Quindi avremo un’interfaccia Shape
e le sue implementazioni concrete come Line
e Oval
. La classe Oval avrà una proprietà intrinseca per determinare se riempire o meno l’ovale con il colore specificato, mentre la Linea non avrà alcuna proprietà intrinseca.
Interfaccia e Classi Concrete del Pattern Flyweight
Shape.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);
}
Line.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");
//aggiunta ritardo temporale
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);
}
}
Oval.java
package com.journaldev.design.flyweight;
import java.awt.Color;
import java.awt.Graphics;
public class Oval implements Shape {
//proprietà intrinseca
private boolean fill;
public Oval(boolean f){
this.fill=f;
System.out.println("Creating Oval object with fill="+f);
//aggiunta ritardo temporale
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);
}
}
}
Nota che ho intenzionalmente introdotto un ritardo nella creazione dell’oggetto delle classi concrete per dimostrare che il pattern flyweight può essere utilizzato per oggetti che richiedono molto tempo durante l’istanziazione.
Factory Flyweight
La factory flyweight verrà utilizzata dai programmi client per istanziare l’oggetto, quindi dobbiamo mantenere una mappa degli oggetti nella factory che non deve essere accessibile dall’applicazione client. Ogni volta che il programma client chiama per ottenere un’istanza dell’oggetto, questa dovrebbe essere restituita dall’HashMap; se non trovata, viene creata un nuovo oggetto e inserita nella mappa, per poi restituirla. Dobbiamo assicurarci che tutte le proprietà intrinseche siano considerate durante la creazione dell’oggetto. La nostra classe factory flyweight appare come il seguente codice. 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;
}
}
Nota l’uso dell’Enum Java per la sicurezza del tipo, Composizione Java (mappa delle forme) e il pattern Factory nel metodo getShape
.
Esempio di Cliente del Modello di Progettazione Flyweight
Sotto è riportato un programma di esempio che utilizza l’implementazione del pattern flyweight. 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
E questo è tutto per il pattern flyweight, esamineremo altri pattern di progettazione in future pubblicazioni. Se ti è piaciuto, condividi le tue opinioni nella sezione commenti e condividilo anche con gli altri.
Esempio di Modello di Progettazione Flyweight in JDK
Tutte le classi wrapper il metodo valueOf()
utilizza oggetti memorizzati nella cache mostrando l’uso del pattern di progettazione Flyweight. Il miglior esempio è la classe String di Java con l’implementazione del Pool di Stringhe.
Importanti Punti del Modello di Progettazione Flyweight
- Nel nostro esempio, il codice del cliente non è obbligato a creare un oggetto utilizzando la fabbrica Flyweight, ma possiamo imporlo per assicurarci che il codice del cliente utilizzi l’implementazione del pattern flyweight; tuttavia, è una decisione di progettazione completa per un’applicazione specifica.
- Il pattern Flyweight introduce complessità e, se il numero di oggetti condivisi è elevato, si verifica uno scambio tra memoria e tempo; quindi è necessario utilizzarlo saggiamente in base ai nostri requisiti.
- L’implementazione del pattern Flyweight non è utile quando il numero di proprietà intrinseche dell’oggetto è elevato, rendendo complessa l’implementazione della classe Factory.
Ecco tutto per il design pattern Flyweight in Java.
Source:
https://www.digitalocean.com/community/tutorials/flyweight-design-pattern-java