Modèle de conception Flyweight en Java

Aujourd’hui, nous examinerons le modèle de conception Flyweight.

Modèle de conception Flyweight

Selon GoF, l’objectif du modèle de conception Flyweight est:

Utiliser le partage pour prendre en charge efficacement un grand nombre d’objets fins

Le modèle de conception Flyweight est un modèle de conception structurel comme le modèle de conception Facade, le modèle de conception Adapter et le modèle de conception Decorator. Le modèle de conception Flyweight est utilisé lorsque nous avons besoin de créer beaucoup d’objets d’une classe. Étant donné que chaque objet consomme de l’espace mémoire, ce qui peut être crucial pour les appareils à faible mémoire, tels que les appareils mobiles ou les systèmes embarqués, le modèle de conception Flyweight peut être appliqué pour réduire la charge sur la mémoire en partageant des objets. Avant d’appliquer le modèle de conception Flyweight, nous devons prendre en compte les facteurs suivants:

  • Le nombre d’objets à créer par l’application devrait être énorme.
  • La création d’objets est lourde en termes de mémoire et peut également prendre du temps.
  • Les propriétés d’un objet peuvent être divisées en propriétés intrinsèques et extrinsèques, les propriétés extrinsèques d’un objet devant être définies par le programme client.

Pour appliquer le modèle de poids mouche, nous devons diviser les propriétés de l’objet en propriétés intrinsèques et extrinsèques. Les propriétés intrinsèques rendent l’objet unique tandis que les propriétés extrinsèques sont définies par le code client et utilisées pour effectuer différentes opérations. Par exemple, un objet cercle peut avoir des propriétés extrinsèques telles que la couleur et la largeur. Pour appliquer le modèle de poids mouche, nous devons créer une fabrique de poids mouche qui renvoie les objets partagés. Pour notre exemple, supposons que nous devons créer un dessin avec des lignes et des ovales. Nous aurons donc une interface Shape et ses implémentations concrètes comme Line et Oval. La classe Oval aura une propriété intrinsèque pour déterminer s’il faut remplir l’ovale avec la couleur donnée ou non, tandis que Line n’aura aucune propriété intrinsèque.

Interface et classes concrètes du modèle de conception du poids mouche

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");
		//ajout d'un délai de temps
		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 {
	
	//propriété intrinsèque
	private boolean fill;
	
	public Oval(boolean f){
		this.fill=f;
		System.out.println("Creating Oval object with fill="+f);
		//ajout d'un délai de temps
		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);
		}
	}

}

Notez que j’ai intentionnellement introduit un délai dans la création de l’objet des classes concrètes pour souligner que le modèle de poids-mouche peut être utilisé pour les objets qui prennent beaucoup de temps à être instanciés.

Usine de poids-mouche

L’usine de poids-mouche sera utilisée par les programmes clients pour instancier l’objet, nous devons donc conserver une carte des objets dans l’usine qui ne devrait pas être accessible par l’application cliente. Chaque fois que le programme client appelle pour obtenir une instance de l’objet, elle doit être renvoyée depuis la table de hachage. Si elle n’est pas trouvée, un nouvel objet doit être créé et placé dans la carte, puis renvoyé. Nous devons nous assurer que toutes les propriétés intrinsèques sont prises en compte lors de la création de l’objet. Notre classe d’usine de poids-mouche ressemble au code ci-dessous. 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;
	}
}

Remarquez l’utilisation de l’énumération Java pour la sécurité des types, la composition Java (carte des formes) et le modèle d’usine dans la méthode getShape.

Exemple de client du modèle de conception Flyweight

Voici un programme d’exemple qui utilise l’implémentation du modèle de conception 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

C’est tout pour le modèle de conception flyweight, nous examinerons d’autres modèles de conception dans de futurs articles. Si vous avez aimé, veuillez partager vos réflexions dans la section des commentaires et le partager avec d’autres également.

Exemple de modèle de conception Flyweight dans JDK

Toutes les classes enveloppantes de la méthode valueOf() utilisent des objets mis en cache montrant l’utilisation du modèle de conception Flyweight. Le meilleur exemple est la classe Java String avec l’implémentation du String Pool.

Points importants du modèle de conception Flyweight

  1. Dans notre exemple, le code client n’est pas obligé de créer un objet en utilisant une usine Flyweight, mais nous pouvons le forcer pour nous assurer que le code client utilise une implémentation de modèle Flyweight. Cependant, c’est une décision de conception complète pour une application particulière.
  2. Le modèle Flyweight introduit de la complexité et s’il y a un grand nombre d’objets partagés, il y a un compromis entre la mémoire et le temps, il faut donc l’utiliser judicieusement en fonction de nos besoins.
  3. L’implémentation du modèle Flyweight n’est pas utile lorsque le nombre de propriétés intrinsèques de l’objet est énorme, ce qui rend l’implémentation de la classe Factory complexe.

C’est tout pour le modèle de conception Flyweight en Java.

Source:
https://www.digitalocean.com/community/tutorials/flyweight-design-pattern-java