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.
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ę.