Wiosenna WYPRZEDAŻ
Pyłek

Pyłek w języku Java

Pyłek to strukturalny wzorzec projektowy umożliwiający obsługę wielkich ilości obiektów przy jednoczesnej oszczędności pamięci.

Wzorzec Pyłek umożliwia zmniejszenie wymogów w zakresie pamięci RAM poprzez współdzielenie części opisu stanu przez wiele obiektów. Innymi słowy Pyłek przechowuje w pamięci podręcznej te dane, które są wspólne dla wielu różnych obiektów.

Złożoność:

Popularność:

Przykłady użycia: Wzorzec Pyłek ma jedno zastosowanie: minimalizacja zużycia pamięci. Możesz póki co zignorować ten wzorzec jeśli twój program nie cierpi na niedostatek pamięci.

Przykłady użycia wzorca Pyłek w głównych bibliotekach Java:

Identyfikacja: Pyłek można poznać po obecności metody kreacyjnej zwracającej obiekty z pamięci podręcznej, zamiast nowo utworzonych.

Renderowanie lasu

W poniższym przykładzie renderujemy las (1 000 000 drzew)! Każde z drzew będzie reprezentowane przez jeden obiekt posiadający pewien stan (współrzędne, tekstura, itd.). Mimo że program wykonuje swoje zadanie, to zużywa przy tym mnóstwo pamięci RAM.

Przyczyna jest prosta: zbyt wiele obiektów-drzew zawiera identyczne dane (nazwa, tekstura, barwa). Możemy więc zastosować wzorzec Pyłek by przenieść te elementy stanu do osobnych obiektów-pyłków (klasa TreeType). Dzięki temu nie musimy przechowywać tych samych danych w tysiącach obiektów Tree, a jedynie odniesienie do odpowiedniego obiektu-pyłka zawierającego dany zestaw wartości.

Z punktu widzenia kodu klienta nie będzie widać zmiany, bo mechanizm odpowiadający za ponowne wykorzystanie pyłków jest umieszczony w fabryce pyłków.

trees

trees/Tree.java: Zawiera unikalny stan każdego drzewa

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: Zawiera stan współdzielony przez wiele drzew

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: Hermetyzuje złożoność tworzenia pyłków

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: Las który rysujemy

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: Kod klienta

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: Zrzut ekranu

OutputDemo.txt: Statystyki zużycia RAM

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

Pyłek w innych językach

Pyłek w języku C# Pyłek w języku C++ Pyłek w języku Go Pyłek w języku PHP Pyłek w języku Python Pyłek w języku Ruby Pyłek w języku Rust Pyłek w języku Swift Pyłek w języku TypeScript