플라이웨이트 는 구조 패턴이며 프로그램들이 객체들의 메모리 소비를 낮게 유지하여 방대한 양의 객체들을 지원할 수 있도록 합니다.
이 패턴은 여러 객체 사이의 객체 상태를 공유하여 위를 달성합니다. 다르게 설명하자면 플라이웨이트는 다른 객체들이 공통으로 사용하는 데이터를 캐싱하여 RAM을 절약합니다.
복잡도:
인기도:
사용 사례들: 플라이웨이트 패턴의 유일한 목적은 메모리 섭취를 최소화하는 것입니다. 당신의 프로그램이 RAM 부족으로 문제를 겪지 않는다면 당분간 이 패턴을 무시할 수 있습니다.
핵심 자바 라이브러리의 플라이웨이트 예시들:
식별: 플라이웨이트는 새로운 객체들 대신 캐싱 된 객체들을 반환하는 생성 메서드의 유무로 식별될 수 있습니다.
숲을 렌더링하기
이 예시에서 우리는 숲(백만 그루의 나무)을 렌더링할 것입니다! 각 트리는 자체 객체로 표시되며 이 객체는 일부 상태(좌표, 질감 등)를 가집니다. 현재 프로그램은 기본 작업은 수행하나, 많은 양의 RAM을 소모합니다.
그 이유는 너무 많은 트리 객체들이 중복된 데이터(예: 이름, 질감, 색상)를 포함하기 때문입니다. 그러므로 플라이웨이트 패턴을 적용하고 위 값들을 별도의 플라이웨이트 객체(TreeType
클래스)에 저장할 수 있습니다. 이제 우리는 수천 개의 Tree
객체들에 같은 데이터를 저장하는 대신 특정 값들의 집합을 지닌 플라이웨이트 객체 중 하나를 참조합니다.
클라이언트 코드는 아무것도 눈치채지 못할 것입니다. 왜냐하면 플라이웨이트 객체들 재사용의 복잡성은 플라이웨이트 팩토리 내부에 묻혀 있기 때문입니다.
trees trees/Tree.java: 각 트리에 대해 고유한 상태를 포함합니다
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: 여러 트리가 공유하는 상태를 포함합니다
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: 플라이웨이트 생성의 복잡성을 캡슐화합니다
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: 우리가 그려야 할 숲
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: 클라이언트 코드
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: 스크린샷
OutputDemo.txt: RAM 사용 통계들
1000000 trees drawn
---------------------
Memory usage:
Tree size (8 bytes) * 1000000
+ TreeTypes size (~30 bytes) * 2
---------------------
Total: 7MB (instead of 36MB)