SOLDES de printemps
Poids mouche

Poids mouche en Java

Le poids mouche est un patron de conception structurel qui permet à des programmes de limiter leur consommation de mémoire malgré un très grand nombre d’objets.

Ce patron est obtenu en partageant des parties de l’état d’un objet à plusieurs autres objets. En d’autres termes, le poids mouche économise de la RAM en mettant en cache les données identiques chez différents objets.

Complexité :

Popularité :

Exemples d’utilisation : Le poids mouche n’a qu’une seule utilité : minimiser la consommation de mémoire. Si votre programme ne rencontre aucun problème de RAM, ignorez ce patron pour le moment.

Voici quelques exemples tirés des bibliothèques principales de Java :

Identification : Le poids mouche peut être reconnu par une méthode de création qui renvoie des objets du cache plutôt que d’en créer de nouveaux.

Afficher une forêt

Dans cet exemple, nous allons procéder à l’affichage d’une forêt (1 000 000 d’arbres) ! Chaque arbre sera représenté par son propre objet avec un certain état (coordonnées, texture, etc.). Même si le programme remplit bien son rôle, il consomme naturellement énormément de RAM.

La raison est simple : de trop nombreux objets Arbre contiennent des données dupliquées (nom, texture, couleur). Nous pouvons lui appliquer le poids mouche afin de stocker ces valeurs dans des objets poids mouche séparés (la classe TypeArbre). Plutôt que de stocker les mêmes données des milliers de fois dans des objets Arbre, nous allons référencer un objet poids mouche avec certaines valeurs.

Le code client ne s’en rendra même pas compte puisque la complexité de la réutilisation des objets poids mouche est cachée à l’intérieur d’une fabrique de poids mouches.

trees

trees/Tree.java: Contient un état unique pour chaque arbre

package refactoring_guru.flyweight.example.trees;

import java.awt.*;

public class Tree {
    private int x;
    private int y;
    private TreeType type;

    public Tree(int x, int y, TreeType type) {
        this.x = x;
        this.y = y;
        this.type = type;
    }

    public void draw(Graphics g) {
        type.draw(g, x, y);
    }
}

trees/TreeType.java: Contient un état partagé entre plusieurs arbres

package refactoring_guru.flyweight.example.trees;

import java.awt.*;

public class TreeType {
    private String name;
    private Color color;
    private String otherTreeData;

    public TreeType(String name, Color color, String otherTreeData) {
        this.name = name;
        this.color = color;
        this.otherTreeData = otherTreeData;
    }

    public void draw(Graphics g, int x, int y) {
        g.setColor(Color.BLACK);
        g.fillRect(x - 1, y, 3, 5);
        g.setColor(color);
        g.fillOval(x - 5, y - 10, 10, 10);
    }
}

trees/TreeFactory.java: Encapsule la complexité de la création de poids mouches

package refactoring_guru.flyweight.example.trees;

import java.awt.*;
import java.util.HashMap;
import java.util.Map;

public class TreeFactory {
    static Map<String, TreeType> treeTypes = new HashMap<>();

    public static TreeType getTreeType(String name, Color color, String otherTreeData) {
        TreeType result = treeTypes.get(name);
        if (result == null) {
            result = new TreeType(name, color, otherTreeData);
            treeTypes.put(name, result);
        }
        return result;
    }
}

forest

forest/Forest.java: La forêt que nous dessinons

package refactoring_guru.flyweight.example.forest;

import refactoring_guru.flyweight.example.trees.Tree;
import refactoring_guru.flyweight.example.trees.TreeFactory;
import refactoring_guru.flyweight.example.trees.TreeType;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

public class Forest extends JFrame {
    private List<Tree> trees = new ArrayList<>();

    public void plantTree(int x, int y, String name, Color color, String otherTreeData) {
        TreeType type = TreeFactory.getTreeType(name, color, otherTreeData);
        Tree tree = new Tree(x, y, type);
        trees.add(tree);
    }

    @Override
    public void paint(Graphics graphics) {
        for (Tree tree : trees) {
            tree.draw(graphics);
        }
    }
}

Demo.java: Code client

package refactoring_guru.flyweight.example;

import refactoring_guru.flyweight.example.forest.Forest;

import java.awt.*;

public class Demo {
    static int CANVAS_SIZE = 500;
    static int TREES_TO_DRAW = 1000000;
    static int TREE_TYPES = 2;

    public static void main(String[] args) {
        Forest forest = new Forest();
        for (int i = 0; i < Math.floor(TREES_TO_DRAW / TREE_TYPES); i++) {
            forest.plantTree(random(0, CANVAS_SIZE), random(0, CANVAS_SIZE),
                    "Summer Oak", Color.GREEN, "Oak texture stub");
            forest.plantTree(random(0, CANVAS_SIZE), random(0, CANVAS_SIZE),
                    "Autumn Oak", Color.ORANGE, "Autumn Oak texture stub");
        }
        forest.setSize(CANVAS_SIZE, CANVAS_SIZE);
        forest.setVisible(true);

        System.out.println(TREES_TO_DRAW + " trees drawn");
        System.out.println("---------------------");
        System.out.println("Memory usage:");
        System.out.println("Tree size (8 bytes) * " + TREES_TO_DRAW);
        System.out.println("+ TreeTypes size (~30 bytes) * " + TREE_TYPES + "");
        System.out.println("---------------------");
        System.out.println("Total: " + ((TREES_TO_DRAW * 8 + TREE_TYPES * 30) / 1024 / 1024) +
                "MB (instead of " + ((TREES_TO_DRAW * 38) / 1024 / 1024) + "MB)");
    }

    private static int random(int min, int max) {
        return min + (int) (Math.random() * ((max - min) + 1));
    }
}

OutputDemo.png: Impression d’écran

OutputDemo.txt: Statistiques d’utilisation de la RAM

1000000 trees drawn
---------------------
Memory usage:
Tree size (8 bytes) * 1000000
+ TreeTypes size (~30 bytes) * 2
---------------------
Total: 7MB (instead of 36MB)

Poids mouche dans les autres langues

Poids mouche en C# Poids mouche en C++ Poids mouche en Go Poids mouche en PHP Poids mouche en Python Poids mouche en Ruby Poids mouche en Rust Poids mouche en Swift Poids mouche en TypeScript