[Design Patterns] Flyweight
Flyweight Pattern là gì?
Flyweight is a structural design pattern that lets you fit more objects into the available amount of RAM by sharing common parts of state between multiple objects instead of keeping all of the data in each object.
- Flyweight giúp chúng ta tối ưu việc khởi tạo một số lượng lớn đối tượng bằng cách share những
dữ liệu giống nhau và không bị thay đổigiữa các object.
Để áp dụng Flyweight Patter, chúng ta cần tách/xác định 2 loại properties của tối tượng gốc:
-
1st là dữ liệu có thể thay đổi - Đây là dữ liệu thể hiện state của một đối tượng, thể hiện sự khác biệt giữa các đối tượng.
Ví dụ: Toạ độ của một cái cây trên màn hình
-
2nd là dữ liệu không thay đổi - Đây là loại dữ liệu sẽ không thay đổi trong suốt vòng đời của object
Ví dụ: Khi chúng ta muốn vẽ một cánh rừng trong game, mỗi cái cây sẽ được xác định màu sắc, kích thước và hình ảnh. Những cái cây cùng loại tất nhiên sẽ có cùng những giá trị này
Lúc này, những dữ liệu ở kiểu thứ 2 có thể gộp lại thành một object, chỉ cần khởi tạo 1 lần và sử dụng lại ở tất cả các object có cùng kiểu - Đây chính là Flyweight object.
Implementation
package com.jacktt.designpatterns.structural.flyweight;
public class TreeType {
private final String name;
private final Integer width;
private final Integer height;
private final String base64Image;
public String getName() {
return name;
}
public Integer getWidth() {
return width;
}
public Integer getHeight() {
return height;
}
public String getBase64Image() {
return base64Image;
}
public TreeType(String name, Integer width, Integer height, String base64Image) {
try {
System.out.println("Initializing TreeType...");
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
this.name = name;
this.width = width;
this.height = height;
this.base64Image = base64Image;
}
}
package com.jacktt.designpatterns.structural.flyweight;
public class Tree {
private TreeType type;
private Integer id;
private Integer x, y;
public Tree(int id, TreeType type, Integer x, Integer y) {
this.id = id;
this.type = type;
this.x = x;
this.y = y;
}
public void draw() {
System.out.println("Tree #" + this.id + " [x: " + this.x + ", y: " + y + "]" + " | Tree type: " + this.type.getName() + " ("+ this.type.hashCode()+ ")");
}
}
package com.jacktt.designpatterns.structural.flyweight;
import java.util.HashMap;
import java.util.Map;
public class TreeTypeFactory {
private static final Map<String, TreeType> treeTypeByName = new HashMap<>();
public static TreeType getInstance(String name, int width, int height, String base64Image) {
TreeType type = treeTypeByName.get(name);
if (type == null) {
type = new TreeType(name, width, height, base64Image);
treeTypeByName.put(name, type);
}
return type;
}
}
package com.jacktt;
import com.jacktt.designpatterns.structural.flyweight.Tree;
import com.jacktt.designpatterns.structural.flyweight.TreeType;
import com.jacktt.designpatterns.structural.flyweight.TreeTypeFactory;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Main {
public static void main(String[] args) {
Date startTime = new Date();
List<Tree> trees = new ArrayList<>();
for (int i = 0; i < 100_000; i++) {
TreeType type;
if (i % 2 == 0) {
type = TreeTypeFactory.getInstance("Red Maple", 20, 100, "Long base64 data");
} else {
type = TreeTypeFactory.getInstance("Scarlet Oak", 35, 120, "Long base64 data");
}
trees.add(new Tree(i, type, i, i * 2));
}
trees.forEach(Tree::draw);
System.out.println("Completed in " + (new Date().getTime() - startTime.getTime()) + "ms");
}
}
...
Tree #99990 [x: 99990, y: 199980] | Tree type: Red Maple (1057941451)
Tree #99991 [x: 99991, y: 199982] | Tree type: Scarlet Oak (873415566)
Tree #99992 [x: 99992, y: 199984] | Tree type: Red Maple (1057941451)
Tree #99993 [x: 99993, y: 199986] | Tree type: Scarlet Oak (873415566)
Tree #99994 [x: 99994, y: 199988] | Tree type: Red Maple (1057941451)
Tree #99995 [x: 99995, y: 199990] | Tree type: Scarlet Oak (873415566)
Tree #99996 [x: 99996, y: 199992] | Tree type: Red Maple (1057941451)
Tree #99997 [x: 99997, y: 199994] | Tree type: Scarlet Oak (873415566)
Tree #99998 [x: 99998, y: 199996] | Tree type: Red Maple (1057941451)
Tree #99999 [x: 99999, y: 199998] | Tree type: Scarlet Oak (873415566)
Completed in 2519ms
Output cho thấy:
-
Hash của các
TreeTypecó cùng name là như nhau, tức là chúng ta đang trỏ tới cùng một TreeType instance trong bộ nhớ.Vì vậy, tổng memory sử dụng cho các instance TreeType chỉ có 2, trong khi nếu không áp dụng Pattern này, tổng memory sẽ là 100_000 nhân size của một TreeType.
-
Tổng thời gain khởi tạo và vẽ ra
100_000Tree chỉ mất2,5s. Trong khi thời gian khởi tạo của mỗi TreeType là 1s. Tức là chúng ta đã tiết kiệm được99,998sso với cách khởi tạo TreeType mới cho mỗi Tree.
Real-world application
- Áp dụng khi chúng ta cần tạo một số lượng lớn các đối tượng của 1 lớp nào đó mà các đối tượng đó có những dữ liệu immutable giống nhau.
java.lang.Integer#valueOf(int)(alsoBoolean, Byte, Character, Short, Long and BigDecimal)
Pros & Cons

Tại sao nó lại tên là Flyweight?
Trong ngành công nghiệp sản xuất và tiêu dùng, "flyweight" là một thuật ngữ được sử dụng để chỉ các sản phẩm nhẹ và có thể được vận chuyển dễ dàng.
All rights reserved