겨울 세일!
플라이웨이트

자바로 작성된 플라이웨이트

플라이웨이트는 구조 패턴이며 프로그램들이 객체들의 메모리 소비를 낮게 유지하여 방대한 양의 객체들을 지원할 수 있도록 합니다.

이 패턴은 여러 객체 사이의 객체 상태를 공유하여 위를 달성합니다. 다르게 설명하자면 플라이웨이트는 다른 객체들이 공통으로 사용하는 데이터를 캐싱하여 RAM을 절약합니다.

복잡도:

인기도:

사용 사례들: 플라이웨이트 패턴의 유일한 목적은 메모리 섭취를 최소화하는 것입니다. 당신의 프로그램이 RAM 부족으로 문제를 겪지 않는다면 당분간 이 패턴을 무시할 수 있습니다.

핵심 자바 라이브러리의 플라이웨이트 예시들:

식별: 플라이웨이트는 새로운 객체들 대신 캐싱 된 객체들을 반환하는 생성 메서드의 유무로 식별될 수 있습니다.

숲을 렌더링하기

이 예시에서 우리는 숲​(백만 그루의 나무)​을 렌더링할 것입니다! 각 트리는 자체 객체로 표시되며 이 객체는 일부 상태​(좌표, 질감 등)​를 가집니다. 현재 프로그램은 기본 작업은 수행하나, 많은 양의 RAM을 소모합니다.

그 이유는 너무 많은 트리 객체들이 중복된 데이터​(예: 이름, 질감, 색상)​를 포함하기 때문입니다. 그러므로 플라이웨이트 패턴을 적용하고 위 값들을 별도의 플라이웨이트 객체​(Tree­Type 클래스)​에 저장할 수 있습니다. 이제 우리는 수천 개의 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)

다른 언어로 작성된 플라이웨이트

C#으로 작성된 플라이웨이트 C++로 작성된 플라이웨이트 Go로 작성된 플라이웨이트 PHP로 작성된 플라이웨이트 파이썬으로 작성된 플라이웨이트 루비로 작성된 플라이웨이트 러스트로 작성된 플라이웨이트 스위프트로 작성된 플라이웨이트 타입스크립트로 작성된 플라이웨이트