본문 바로가기
Study/디자인 패턴

[C#/Unity][디자인패턴] 경량 패턴(Flyweight Pattern)

by 스테디코디스트 2023. 12. 1.
반응형

1. 경량 패턴이란?

- 재사용 가능한 객체 인스턴스를 공유시켜 메모리 사용량을 최소화하는 구조 패턴.

- 한 개의 고유 상태를 다른 객체들에서 공유하게 만들어 메모리 사용량을 줄이는  패턴

- 내용이 같은 객체가 이미 있으면 새로 객체를 만들지 않고, 내용이 같은 기존 객체를 공유한다.

- 동일하거나 유사한 객체들 사이에 가능한 많은 데이터를 서로 공유하며 사용하도록 메모리 사용량을 최소화하는 패턴.

- 반복된 데이터에 대해 하드웨어의 부담을 줄여주는 패턴

 

2. 경량  패턴을 사용하는 경우

- 메모리를 최소한으로 사용해야하는 경우

- 공통적인 인스턴스를 많이 생성하는 로직이 포함된 경우

- 생성된 객체가 오래도록 메모리에 상주하며 사용되는 횟수가 많은 경우

- 어플리케이션에 의해 생성되는 객체의 수가 많아 저장 비용이 높아지는 경우

- 게임에서 숲이나 유닛, 지형, 자잘한 환경 오브젝트 등 서로 비슷한 요소를 가지면서 많은 객체를 필요로 할 때 리소스의 최적화를 위해 사용

 

3. 경량 패턴의 장점

- 공통적인 속성을 갖는 객체 생성시 메모리 양을 줄일 수 있음.

- 프로그램 속도를 개선할 수 있음.

 

4. 경량 패턴의 단점

- 코드의 복잡성 증가

- 데이터를 서로 공유하며 사용하도록 구현되므로, 해당 패턴에 포함된 객체들 중에서 일부만 다른 식으로 동작하도록 구현하는 것에는 무리가 있다.

 

5. 코드 구현(C#)

1) 패턴 적용 전 소스코드

using System;

namespace StudyCSharp
{
    class Memory
    {
        public static long size = 0; // 메모리 사용량

        public static void print()
        {
            Console.WriteLine("총 메모리 사용량 : " + Memory.size + "MB");
        }
    }

    class Tree
    {
        long objSize = 100; // 메모리 100MB 사용

        string _type; // 나무 종류

        Object _mesh; // 메쉬
        Object _texture; // 나무 껍질 + 잎사귀 텍스쳐

        // 위치 변수
        public double _posX;
        public double _posY;

        public Tree(string type, Object mesh, Object texture, double posX, double posY)
        {
            this._type = type;
            this._mesh = mesh;
            this._texture = texture;
            this._posX = posX;
            this._posY = posY;

            // 객체를 생성하였으므로 메모리 사용량 증가
            Memory.size += this.objSize;
        }
    }

    class Terrain
    {
        // 지형 타일의 크기
        public const int CANVAS_SIZE = 1000;

        // 렌더링 함수
        public void rendering(string type, Object mesh, Object texture, double posX, double posY)
        {
            // 나무 객체 생성
            Tree tree = new Tree(type, mesh, texture, posX, posY);

            Console.WriteLine("x: {0}\ty: {1}\t위치에 {2} 나무 생성 완료", tree._posX, tree._posY, type);
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            // 지형 생성
            Terrain terrain = new Terrain();

            // 랜덤수 생성
            Random random = new Random();

            // 지형에 Oak 나무 5그루 생성
            for (int i = 0; i< 5; i++)
            {
                terrain.rendering("Oak", new object(), new object(),
                    (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0, (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0);
            }
            Console.WriteLine();

            // 지형에 Acacia 나무 5그루 생성
            for (int i = 0; i < 5; i++)
            {
                terrain.rendering("Acacia", new object(), new object(),
                   (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0, (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0);
            }
            Console.WriteLine();

            // 지형에 Jungle 나무 5그루 생성
            for (int i = 0; i < 5; i++)
            {
                terrain.rendering("Jungle", new object(), new object(),
                    (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0, (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0);
            }
            Console.WriteLine();

            // 메모리 사용량 출력
            Memory.print();
            Console.WriteLine();
        }
    }
}

 

- 실행결과 : 100MB의 나무 15그루를 생성했으므로 15x100MB = 1500MB의 메모리가 사용된다.

 

2) 패턴 적용 후 소스코드(c#)

using System;
using System.Collections.Generic;

namespace StudyCSharp
{
    class Memory
    {
        public static long size = 0; // 메모리 사용량

        public static void print()
        {
            Console.WriteLine("총 메모리 사용량 : " + size + "MB");
        }
    }

    // UnsharedConcreteFlyweight : 공유되지 않는 속성값들
    class Tree
    {
        // 나무의 위치값과 나무모델 참조 객체의 크기를 합친 메모리의 크기
        long objSize = 10; // 10MB

        // 위치 변수
        public double _posX;
        public double _posY;

        // 나무 모델
        TreeModel _model;

        public Tree(TreeModel model, double posX, double posY)
        {
            this._model = model;
            this._posX = posX;
            this._posY = posY;

            // 객체를 생성하였으므로 메모리 사용량 증가
            Memory.size += this.objSize;
        }
    }

    // ConcreteFlyweight : 공유되는 속성값들
    // 플라이웨이트 객체는 불변성을 가져야하기 때문에 sealed 클래스를 사용했음.
    sealed class TreeModel
    {
        // 위치값을 제외한 나머지 메모리 크기
        long objSize = 90; // 90MB

        string _type; // 나무 종류

        object _mesh; // 메쉬
        object _texture; // 나무 껍질 + 잎사귀 텍스쳐

        public TreeModel(string type, object mesh, object texture)
        {
            this._type = type;
            this._mesh = mesh;
            this._texture = texture;

            // 메모리 사용량 증가
            Memory.size += this.objSize;
        }
    }

    // FlyweightFactory : 플라이웨이트 팩토리
    // 1. Flyweight Pool : HashMap 컬렉션을 통해 키(key)와 나무 모델 객체를 저장하는 캐시 저장소 역할
    // 2. getInstance 메서드 : Pool에 가져오고자 하는 객체가 있는지를 검사하여 있으면 그대로 반환, 없으면 새로 생성
    class TreeModelFactory
    {
        // Flyweight Pool - TreeModel 객체들을 Map으로 등록하여 캐싱
        private static readonly Dictionary<string, TreeModel> cache = new Dictionary<string, TreeModel>();

        public static TreeModel GetInstance(string key)
        {
            // 이미 캐시가 되어있는 경우
            if (cache.ContainsKey(key))
            {
                Console.Write("재사용 객체 사용\t");
                return cache[key];
            }
            else // 캐시 되어있지 않은 경우 -> 나무 모델 객체를 새로 생성 후 반환
            {
                TreeModel model = new TreeModel(key, new object(), new object());

                Console.Write("-객체 새로 생성-\t");

                // 캐시에 적재
                cache.Add(key, model);

                // 객체 반환
                return model;
            }
        }
    }

    class Terrain
    {
        // 지형 타일의 크기
        public const int CANVAS_SIZE = 1000;

        // 렌더링 함수
        public void rendering(string type, double posX, double posY)
        {
            // 1. 캐시 되어 있는 나무 모델 객체 가져오기
            TreeModel model = TreeModelFactory.GetInstance(type);

            // 2. 재사용한 나무 모델 객체와 변하는 속성인 위치값을 이용해 나무 객체 생성
            Tree tree = new Tree(model, posX, posY);

            Console.WriteLine("x: {0}\ty: {1}\t위치에 {2} 나무 생성 완료", tree._posX, tree._posY, type);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 지형 생성
            Terrain terrain = new Terrain();

            // 랜덤수 생성
            Random random = new Random();

            // 지형에 Oak 나무 5그루 생성
            for (int i = 0; i< 5; i++)
            {
                terrain.rendering("Oak",
                    (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0, (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0);
            }
            Console.WriteLine();

            // 지형에 Acacia 나무 5그루 생성
            for (int i = 0; i < 5; i++)
            {
                terrain.rendering("Acacia",
                   (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0, (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0);
            }
            Console.WriteLine();

            // 지형에 Jungle 나무 5그루 생성
            for (int i = 0; i < 5; i++)
            {
                terrain.rendering("Jungle",
                    (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0, (int)(random.NextDouble() * Terrain.CANVAS_SIZE) / 100.0);
            }
            Console.WriteLine();

            // 메모리 사용량 출력
            Memory.print();
            Console.WriteLine();
        }
    }
}

 

- 실행 결과 : 새로 생성시 100MB, 기존 객체 재사용시 10MB의 메모리가 사용되므로 3x100MB+12x10MB = 420MB의 메모리가 사용된다.


<참고 사이트>

 

[디자인패턴] 경량 패턴, 플라이웨이트 패턴 (Flyweight Pattern)

◎ 정의 플라이웨이트 패턴(Flyweight pattern)는 동일하거나 유사한 객체들 사이에 가능한 많은 데이터를 서로 공유하여 사용하도록 하여 메모리 사용량을 최소화하는 소프트웨어 디자인 패턴이다.

blacktopaz.tistory.com

 

플라이웨이트 패턴

/ 디자인 패턴들 / 구조 패턴 플라이웨이트 패턴 다음 이름으로도 불립니다: 캐시, Flyweight 의도 플라이웨이트는 각 객체에 모든 데이터를 유지하는 대신 여러 객체들 간에 상태의 공통 부분들을

refactoring.guru

 

💠 경량(Flyweight) 패턴 - 완벽 마스터하기

Flyweight Pattern 플라이웨이트 패턴(Flyweight Pattern)은 재사용 가능한 객체 인스턴스를 공유시켜 메모리 사용량을 최소화하는 구조 패턴이다. 간단히 말하면 캐시(Cache) 개념을 코드로 패턴화 한것으

inpa.tistory.com

 

Flyweight Pattern, 경량 패턴 [디자인패턴]

우리는 게임을 만들 때 반복작업을 하는 경우가 많다. 웬만해서는 거의 반복작업 위주다. 그렇다면 반복작업하는 시간을 줄이거나 반복 작업을 효율적으로 할 수 있다면 전체 프로젝트의 효율

luv-n-interest.tistory.com

 

Chapter 8. 경량 패턴(Flyweight Pattern)

인프런에 있는 이재환님의 강의 게임 디자인 패턴 with Unity 를 듣고 정리한 필기입니다. 😀

ansohxxn.github.io