본문 바로가기
Thinking/Concept

플라이웨이트 패턴(Flyweight Pattern) 이해하기

by Dev_카페인 2024. 11. 15.
반응형

플라이웨이트 패턴(Flyweight Pattern) 이해하기

플라이웨이트 패턴공통된 속성을 공유하여 메모리 사용을 최소화하는 구조적 디자인 패턴입니다. 이 패턴은 비슷하거나 동일한 객체가 대량으로 생성될 때, 중복 데이터를 공유하여 메모리를 절약하는 데 주로 사용됩니다. 플라이웨이트 패턴을 통해 애플리케이션의 메모리 사용을 최적화하고 성능을 개선할 수 있습니다.

이번 포스트에서는 플라이웨이트 패턴의 개념과 구조, 구현 방법, 장단점, 사용 시점을 구체적인 예제와 함께 살펴보겠습니다.

플라이웨이트 패턴이란?

플라이웨이트 패턴은 반복되는 데이터가 많은 객체를 공유하여 메모리 사용량을 줄이는 패턴입니다. 이 패턴은 객체의 상태를 **내부 상태(Intrinsic State)**와 **외부 상태(Extrinsic State)**로 분리하여, 공통된 내부 상태는 공유하고, 외부 상태는 개별적으로 관리하는 방식으로 구현됩니다.

예를 들어, 많은 캐릭터들이 동일한 이미지를 공유하지만 위치나 크기와 같은 속성만 다르다면, 이미지 객체는 하나만 생성하고 위치와 크기 정보는 각 캐릭터 객체가 개별적으로 관리하는 방식으로 메모리를 절약할 수 있습니다.

 

플라이웨이트 패턴의 구조

플라이웨이트 패턴의 기본 구조는 다음과 같습니다.

  1. Flyweight (플라이웨이트): 내부 상태를 정의하는 인터페이스로, 내부 상태를 공유할 수 있도록 설계됩니다.
  2. ConcreteFlyweight (구체적인 플라이웨이트): Flyweight 인터페이스를 구현하며, 외부 상태가 동일한 객체에 대해 재사용될 수 있도록 설계된 객체입니다.
  3. FlyweightFactory (플라이웨이트 팩토리): 플라이웨이트 객체를 관리하며, 이미 생성된 플라이웨이트 객체를 반환하거나, 새로운 객체를 생성하여 저장하고 반환하는 역할을 합니다.
  4. Client (클라이언트): 플라이웨이트 객체를 사용하며, 객체의 외부 상태를 개별적으로 관리합니다.

플라이웨이트(Flyweight)

플라이웨이트 패턴의 구현 방법

예제 코드: 나무 객체 공유 예제

많은 나무(Tree) 객체가 게임에 배치되는 상황을 예로 들어보겠습니다. 나무의 위치 정보는 개별적으로 다를 수 있지만, 나무의 종류와 색상과 같은 속성은 여러 나무 객체가 공유할 수 있습니다.

// Flyweight 인터페이스
public interface ITree
{
    void Display(int x, int y); // 외부 상태인 위치 정보를 매개변수로 전달
}

// ConcreteFlyweight 클래스 (구체적인 플라이웨이트)
public class TreeType : ITree
{
    private string _type;
    private string _color;

    public TreeType(string type, string color)
    {
        _type = type;
        _color = color;
    }

    public void Display(int x, int y)
    {
        Console.WriteLine($"Displaying {_type} tree of color {_color} at ({x}, {y})");
    }
}

// FlyweightFactory 클래스
public class TreeFactory
{
    private Dictionary<string, ITree> _trees = new Dictionary<string, ITree>();

    public ITree GetTreeType(string type, string color)
    {
        string key = type + "_" + color;

        if (!_trees.ContainsKey(key))
        {
            _trees[key] = new TreeType(type, color);
            Console.WriteLine($"Creating new TreeType: {type} of color {color}");
        }
        return _trees[key];
    }
}

// Client 클래스
class Forest
{
    private List<(ITree, int, int)> _trees = new List<(ITree, int, int)>();
    private TreeFactory _treeFactory = new TreeFactory();

    public void PlantTree(string type, string color, int x, int y)
    {
        ITree tree = _treeFactory.GetTreeType(type, color);
        _trees.Add((tree, x, y));
    }

    public void DisplayTrees()
    {
        foreach (var (tree, x, y) in _trees)
        {
            tree.Display(x, y);
        }
    }
}

// 클라이언트 코드
class Program
{
    static void Main()
    {
        Forest forest = new Forest();
        
        // 여러 나무를 심습니다.
        forest.PlantTree("Oak", "Green", 10, 20);
        forest.PlantTree("Pine", "Dark Green", 15, 25);
        forest.PlantTree("Oak", "Green", 30, 40);
        forest.PlantTree("Pine", "Dark Green", 50, 60);

        forest.DisplayTrees();
    }
}

위 코드에서 TreeType 클래스는 나무의 종류와 색상을 나타내는 공유 객체로, Display() 메서드를 통해 외부 상태인 위치 정보를 표시합니다. TreeFactory 클래스는 이미 생성된 TreeType 객체를 재사용하며, 동일한 속성을 가진 객체가 여러 번 생성되는 것을 방지합니다.

출력 결과는 다음과 같습니다.

Creating new TreeType: Oak of color Green
Creating new TreeType: Pine of color Dark Green
Displaying Oak tree of color Green at (10, 20)
Displaying Pine tree of color Dark Green at (15, 25)
Displaying Oak tree of color Green at (30, 40)
Displaying Pine tree of color Dark Green at (50, 60)

이 예제에서는 Oak와 Pine이라는 두 종류의 나무만 생성되었으며, 나무의 위치 정보는 클라이언트에서 관리하여 메모리를 절약할 수 있습니다. 동일한 나무 타입의 객체를 반복해서 사용하므로, 메모리 사용이 최적화됩니다.

 

플라이웨이트 패턴의 장단점

장점

  1. 메모리 절약: 동일한 객체를 공유함으로써 메모리 사용량을 줄일 수 있습니다.
  2. 성능 향상: 메모리 사용량이 감소하여 객체 생성과 관리에 대한 성능이 향상될 수 있습니다.
  3. 유연한 구조: 객체를 효율적으로 재사용할 수 있어 코드의 확장성이 높아집니다.

단점

  1. 복잡성 증가: 객체를 공유하기 위해 외부 상태와 내부 상태를 분리하여 관리해야 하므로 코드가 복잡해질 수 있습니다.
  2. 객체 관리 비용: 공유 객체와 외부 상태를 관리하기 위한 추가 코드가 필요하며, 잘못된 외부 상태 관리로 인해 버그가 발생할 가능성이 있습니다.

 

언제 플라이웨이트 패턴을 사용해야 할까?

플라이웨이트 패턴은 비슷한 객체가 대량으로 생성되어 메모리 사용을 최적화할 필요가 있을 때 유용합니다. 다음과 같은 경우에 사용하면 좋습니다.

  1. 대량의 유사한 객체가 필요한 경우: 동일한 데이터가 반복되는 대량의 객체를 생성해야 할 때 메모리를 절약할 수 있습니다.
  2. 객체의 일부 상태가 공통된 경우: 객체의 속성 중 일부는 공통되며, 일부 속성만 개별적으로 관리할 때 적합합니다.
  3. 데이터 중복을 최소화하고 싶을 때: 대량의 객체에서 불필요한 데이터 중복을 제거하여 효율적으로 메모리를 사용하고자 할 때 사용할 수 있습니다.

 

플라이웨이트 패턴과 관련 패턴 비교

플라이웨이트 패턴은 객체를 공유하여 메모리를 최적화하는 패턴으로, 특히 싱글턴 패턴과 유사한 목적을 가지지만, 구조와 목적이 다릅니다.

  • 싱글턴 패턴: 하나의 특정 인스턴스만 유지하여 전역에서 접근할 수 있게 합니다. 모든 객체가 동일한 인스턴스에 접근해야 할 때 사용됩니다.
  • 플라이웨이트 패턴: 동일한 유형의 객체가 여러 개 생성되는 것을 방지하기 위해 객체를 공유하여 메모리 사용을 최적화합니다. 내부 상태는 공유하지만 외부 상태는 개별적으로 관리합니다.

따라서, 하나의 객체만 필요할 때는 싱글턴 패턴을, 유사한 객체가 대량으로 필요할 때는 플라이웨이트 패턴을 사용하는 것이 적절합니다.

 

마무리하며

플라이웨이트 패턴은 비슷한 객체가 대량으로 생성될 때 메모리 최적화에 매우 유용한 패턴입니다. 객체의 내부 상태를 공유하고 외부 상태를 분리하여 필요한 속성만 개별적으로 관리함으로써 메모리 사용을 최소화할 수 있습니다. 특히, 게임이나 그래픽 시스템처럼 대량의 객체가 반복적으로 생성되는 애플리케이션에서 성능 향상을 위해 플라이웨이트 패턴을 활용해보세요.

반응형