Pyłek (ang. flyweight) – jest to wzorzec należący do typu wzorców strukturalnych, który pozwala na zaoszczędzenie miejsca w pamięci komputera. Dzięki niemu możemy zmieścić więcej obiektów w dostępnej pamięci RAM, poprzez współdzielenie między wieloma obiektami ich części wspólnych, zamiast trzymanie wszystkich danych w każdym obiekcie z osobna. Klasa, którą współdzielimy musi być niezmienna (ang. immutable) – nie może być w niej setterów, gettery, które zwracają obiekty powinny zwracać ich kopie, a nie referencje do faktycznych obiektów, z których korzystamy.

Jako przykład niech posłuży zestaw klocków składających się z trzech różnych kształtów, każdy po milion elementów występujących w trzech kolorach: żółtym, niebieskim i czerwonym. W sumie dziewięć milinów elementów. Aby utworzyć w pamięci komputera miejsce na te elementy nie musimy generować każdego elementu z osobnym pakietem swoich właściwości, lecz posłużymy się wzorcem flyweight. Każdy kształt klocka posiada zestaw właściwości, które są niezmienne: niech to będzie długość, szerokość, wysokość i liczba modułów w każdym klocku. Wydzielmy więc te właściwości do osobnej klasy – nazwijmy ją „BrickLook”. Następnie tworzymy klasy dla poszczególnych kształtów klocka – „SingleBrick”, „DoubleBrick” i „TripleBrick”. Każda z nich zawiera pole typu „BrickLook” określające niezmienne właściwości dla każdego klocka. Kolejnym krokiem jest utworzenie klasy z katalogiem dostępnych kształtów klocków – „BrickLookRepository”. W klasie tej istnieją prywatne statyczne pola typu „BrickLook” tworzące instancje tej klasy, o danych parametrach, innych dla poszczególnych kształtów. W naszej głównej metodzie tworzymy listę obiektów, do której dodajemy w pętli poszczególne klocki w trzech kolorach.

Diagram UML wzorca Flyweight - Pyłek

BrickLook.java

package flyweight.example.bricks;

public class BrickLook {

    private int height;
    private int length;
    private int width;
    private int amountOfModules;

    public BrickLook(int height, int length, int width, int amountOfModules) {
        this.height = height;
        this.length = length;
        this.width = width;
        this.amountOfModules = amountOfModules;
    }

    public int getHeight() {
        return height;
    }

    public int getLength() {
        return length;
    }

    public int getWidth() {
        return width;
    }

    public int getAmountOfModules() {
        return amountOfModules;
    }
}

SingleBrick.java

package flyweight.example.bricks;

public class SingleBrick {
    private String color;
    private BrickLook look;

    public SingleBrick(String color) {
        this.look = BrickLookRepository.getSingleBrickLook();
        this.color = color;
    }
}

DoubleBrick.java

package flyweight.example.bricks;

public class DoubleBrick {
    private String color;
    private BrickLook look;

    public DoubleBrick(String color) {
        look = BrickLookRepository.getDoubleBrickLook();
        this.color = color;

    }
}

TripleBrick.java

package flyweight.example.bricks;

public class TripleBrick {
    private String color;
    private BrickLook look;

    public TripleBrick(String color) {
        this.look = BrickLookRepository.getTripleBrickLook();
        this.color = color;
    }
}

BrickLookRepository.java

package flyweight.example.bricks;

public class BrickLookRepository {
    private static BrickLook doubleBrickLook = new BrickLook(20, 20, 20, 2);
    private static BrickLook singleBrickLook = new BrickLook(20, 20, 10, 1);
    private static BrickLook tripleBrickLook = new BrickLook(20, 30, 20, 3);
    private BrickLookRepository() {}

    public static BrickLook getSingleBrickLook() {
        return singleBrickLook;
    }
    public static BrickLook getDoubleBrickLook() {
        return doubleBrickLook;
    }
    public static BrickLook getTripleBrickLook() {
        return tripleBrickLook;
    }


}

Main.java

package flyweight.example;

import flyweight.example.bricks.DoubleBrick;
import flyweight.example.bricks.SingleBrick;
import flyweight.example.bricks.TripleBrick;

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Object> setOfBricks = new ArrayList<>();

        for(int i=0; i<1000000; i++) {
            setOfBricks.add(new SingleBrick("yellow"));
            setOfBricks.add(new DoubleBrick("yellow"));
            setOfBricks.add(new TripleBrick("yellow"));

            setOfBricks.add(new SingleBrick("blue"));
            setOfBricks.add(new DoubleBrick("blue"));
            setOfBricks.add(new TripleBrick("blue"));

            setOfBricks.add(new SingleBrick("red"));
            setOfBricks.add(new DoubleBrick("red"));
            setOfBricks.add(new TripleBrick("red"));
        }
    }
}

PODSUMOWANIE

Tak więc w naszym prostym przykładzie każdy klocek wskazuje na jedną instancję klasy określającej wygląd klocka – „BrickLook”. Dzięki użyciu wzorca flyweight każdy z miliona konkretnych klocków wskazuje na jedną instancję stałych cech wyglądu, zawartych w klasie „BrickLook”.  Dzięki temu mamy o cztery miliony pól typu int mniej na każdy stworzony milion klocków. W rzeczywistości spotykamy sytuacje bardziej skomplikowane i rozbudowane. Ponieważ zastosowanie wzorca generuje pracę związaną z doprowadzeniem projektu do stanu, kiedy będziemy mogli użyć wzorca, tak więc dobrze jest mieć jakiś przynajmniej wstępny dowód na to, że spadek pamięci będzie znaczący i zastosowanie wzorca ulepszy nasza aplikację.