ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Flyweight Pattern(플라이웨이트 패턴)
    STUDY/디자인패턴 2025. 1. 7. 20:06

    플라이웨이트 패턴이란?

    각 객체에 모든 데이터를 유지하는 대신 여러 객체들 간에 공통 부분들을 공유하여, 사용할 수 있는 RAM에 더 많은 객체들을 포함할 수 있도록 하는 구조 디자인 패턴

     

    즉, 동일하거나 유사한 객체들 사이에서 공통으로 사용하는 변하지 않는 속성을 분리해 캐싱하여 메모리 사용을 줄이는 방식이다.

     

    Intrinsic 와 Extrinsic 상태

    Intrinsic란 고유한, 본질적인이라는 의미로, 본질적인 상태란 인스턴스가 어떠한 상황에서도 변하지 않는 정보를 말한다. 값이 고정되어 있기 때문에 언제 어디서 공유해도 문제가 없다.(여러 인스턴스 간에 공유 가능한 불변의 데이터)

     

    Extrinsic란 외적인, 비본질적인이란 의미로, 인스턴스를 두는 장소나 상황에 따라 변화하는 정보를 말한다. 언제 어디서 변화할지 모르기 때문에 공유할 수 없다.(각 인스턴스마다 고유한 데이터)

     

    구조

     

    • Flyweight

    플라이웨이트 클래스에는 여러 객체들 간에 공유할 수 있는 Intrinsic 상태가 포함된다. 같은 플라이웨이트 객체가 다양한 콘텍스트에서 사용될 수 있다.

     

    • FlyweightFactory

    기존 플라이웨이트들의 풀을 관리한다. 팩토리는 이전에 생성된 플라이웨이트들을 살펴보고 검색 기준과 일치하는 기존 플라이웨이트를 반환하거나 기준에 맞는 플라이웨이트가 발견되지 않으면 새로 생성한다.

     

    • Context

    콘텍스트 클래스는 인스턴스마다 고유한 extrinsic 상태를 가지며, 하나의 플라이웨이트 객체와 쌍을 이루게 되면 원래 객체의 전체 상태를 나타낸다.

     

    예시

     

    Context

    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);
        }
    }

     

    Flyweight

    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);
        }
    }

     

    FlyweightFactory

    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;
        }
    }

     

    Client

    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);
            }
        }
    }
    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));
        }
    }

     

    적용

    1. 수많은 유사 객체들을 생성하는 경우
    2. 객체 생성 비용이 높은 경우
    3. 객체들이 중복 상태들을 포함하며, 이 상태들을 추출하여 공유할 수 있는 경우

     

    장단점

    장점

    • 프로그램에 유사한 객체가 많을 경우 많은 RAM을 절약할 수 있다,

    단점

    • 플라이웨이트 메서드를 호출할 때마다 콘텍스트 데이터의 일부를 다시 계산해야 한다면 CPU 주기 대신 RAM을 절약하는 것일지도 모른다.
    • 코드가 복잡해지므로 새로운 팀원들은 왜 개체의 상태가 그런 식으로 분리되었는지 파악하기 어려울 수 있다.

     

    참고

    https://refactoring.guru/ko/design-patterns/flyweight

    'STUDY > 디자인패턴' 카테고리의 다른 글

    Proxy Pattern(프록시 패턴)  (0) 2025.01.07
    Facade Pattern(퍼사드 패턴)  (0) 2025.01.07
    Decorator Pattern(데코레이터 패턴)  (0) 2024.12.25
    Composite Pattern(복합체 패턴)  (0) 2024.12.25
    Bridge Pattern(브리지 패턴)  (0) 2024.12.08
Designed by Tistory.