본문 바로가기
컴퓨터 과학/디자인패턴

[디자인패턴] 구조 패턴(4) : 플라이웨이트(Flyweight)

by webcodur 2024. 3. 25.
728x90

목차

     

    플라이웨이트 (Flyweight)

    플라이웨이트(Flyweight) 디자인 패턴은 객체의 효율적인 공유를 통해 대량의 작은 객체들이 사용될 때 발생하는 메모리 사용량을 최소화하는 구조적인 패턴이다. 이 패턴의 목적은 공유를 통해 대량의 객체들 사이에서 발생할 수 있는 불필요한 데이터의 중복을 줄이는 것이다.

    플라이웨이트 패턴의 이름은 '가벼운' 혹은 '무게가 거의 없는'이라는 의미의 'Flyweight'에서 유래되었다. 이는 패턴이 객체의 메모리 사용량을 최소화하여 마치 객체가 '가벼워진' 것처럼 만든다는 개념을 반영한다.

     

    패턴 미적용 예시

    C#으로 작성된 코드에서 플라이웨이트 패턴을 적용하지 않았을 경우, 객체를 매번 새로 생성하게 되어, 동일한 정보를 가진 객체가 중복해서 메모리에 할당될 수 있다. 예를 들어, 특정 문자의 스타일(글꼴, 크기 등) 정보를 저장하는 간단한 문자 객체를 대량으로 생성하는 경우를 생각해보자.

    class Character
    {
        public char Symbol { get; set; }
        public int FontSize { get; set; }
        public string FontName { get; set; }
    
        public Character(char symbol, int fontSize, string fontName)
        {
            Symbol = symbol; //  'A', '1', '@' 등 실제 문자 데이터
            FontSize = fontSize;
            FontName = fontName;
        }
        // 여기에 문자를 화면에 표시하는 등의 메서드가 추가될 수 있다.
    }
    

    이 경우, 동일한 문자에 대해 여러 번 객체를 생성하면 각 객체가 독립적으로 메모리를 차지하게 된다.

     

    패턴 적용 예시

    다음은 플라이웨이트 패턴의 4가지 주요 구성 요소가 명확하게 분리되도록 작성한 코드 예시이다.

    참고로 플라이웨이트 패턴의 주요 구성 요소는 다음과 같다:

    • Flyweight(플라이웨이트): 공유될 객체의 인터페이스를 정의한다.
    • ConcreteFlyweight(구체적 플라이웨이트): Flyweight 인터페이스를 구현하는 클래스로, 내부 상태를 가지며 공유될 수 있다.
    • FlyweightFactory(플라이웨이트 팩토리): Flyweight 객체들의 생성과 관리를 담당하며, 필요에 따라 새로운 객체를 생성하거나 기존 객체를 반환한다.
    • Client(클라이언트): Flyweight 팩토리를 사용하여 Flyweight 객체들을 생성하고 조작한다. 외부 상태를 Flyweight 객체에 제공한다.
    interface IFlyweight
    {
        void Display(string fontName); // 외부 상태
    }
    
    class CharacterFlyweight : IFlyweight
    {
        public char Symbol { get; private set; }
        public int FontSize { get; private set; } // 내부 상태
    
        public CharacterFlyweight(char symbol, int fontSize)
        {
            Symbol = symbol;
            FontSize = fontSize;
        }
    
        public void Display(string fontName) // 외부 상태를 이용한 메서드 구현
        {
            Console.WriteLine($"Displaying {Symbol} in {fontName} with size {FontSize}.");
        }
    }
    
    class FlyweightFactory
    {
        private Dictionary<string, IFlyweight> flyweights = new Dictionary<string, IFlyweight>();
    
        public IFlyweight GetFlyweight(char symbol, int fontSize)
        {
            string key = $"{symbol}:{fontSize}";
    
            if (!flyweights.ContainsKey(key))
            {
                flyweights[key] = new CharacterFlyweight(symbol, fontSize);
            }
    
            return flyweights[key];
        }
    }
    
    class Client
    {
        private FlyweightFactory factory = new FlyweightFactory();
    
        public void DisplayCharacters()
        {
            var characterA = factory.GetFlyweight('A', 12);
            characterA.Display("Arial");
    
            var characterB = factory.GetFlyweight('B', 12);
            characterB.Display("Arial");
    
            // 동일한 'A', 12를 다시 요청할 경우, 기존에 생성된 객체를 재사용한다.
            var characterA2 = factory.GetFlyweight('A', 12);
            characterA2.Display("Times New Roman");
        }
    }
    
    

     

    1. Flyweight 인터페이스

    Flyweight 인터페이스는 공유될 객체의 인터페이스를 정의한다. 이 예시에서는 실제 구현을 포함하지 않고, Display 메서드의 시그니처만 정의한다.

    interface IFlyweight
    {
        void Display(string fontName); // 외부 상태
    }
    

     

    2. ConcreteFlyweight 클래스

    ConcreteFlyweight 클래스는 IFlyweight 인터페이스를 구현하며, 실제로 공유될 객체의 데이터와 행위를 정의한다.

    class CharacterFlyweight : IFlyweight
    {
        public char Symbol { get; private set; }
        public int FontSize { get; private set; } // 내부 상태
    
        public CharacterFlyweight(char symbol, int fontSize)
        {
            Symbol = symbol;
            FontSize = fontSize;
        }
    
        public void Display(string fontName) // 외부 상태를 이용한 메서드 구현
        {
            Console.WriteLine($"Displaying {Symbol} in {fontName} with size {FontSize}.");
        }
    }
    

     

    3. FlyweightFactory 클래스

    FlyweightFactory 클래스는 IFlyweight 객체의 생성과 관리를 담당한다. 필요에 따라 새로운 객체를 생성하거나 기존 객체를 반환한다.

    class FlyweightFactory
    {
        private Dictionary<string, IFlyweight> flyweights = new Dictionary<string, IFlyweight>();
    
        public IFlyweight GetFlyweight(char symbol, int fontSize)
        {
            string key = $"{symbol}:{fontSize}";
    
            if (!flyweights.ContainsKey(key))
            {
                flyweights[key] = new CharacterFlyweight(symbol, fontSize);
            }
            return flyweights[key];
        }
    }
    
    

     

    4. Client 클래스

    클라이언트 클래스는 FlyweightFactory 를 사용하여 IFlyweight 객체를 생성하고 조작한다. 이는 외부 상태를 IFlyweight 객체에 제공하는 역할을 수행한다.

    class Client
    {
        private FlyweightFactory factory = new FlyweightFactory();
    
        public void DisplayCharacters()
        {
            var characterA = factory.GetFlyweight('A', 12);
            characterA.Display("Arial");
    
            var characterB = factory.GetFlyweight('B', 12);
            characterB.Display("Arial");
    
            // 동일한 'A', 12를 다시 요청할 경우, 기존에 생성된 객체를 재사용한다.
            var characterA2 = factory.GetFlyweight('A', 12);
            characterA2.Display("Times New Roman");
        }
    }
    

     

    이 구조에서 Client 클래스는 FlyweightFactory 를 통해 필요한 CharacterFlyweight 객체를 요청하고, 해당 객체의 Display 메서드를 호출하여 외부 상태(여기서는 fontName)를 제공한다.

     

    이 과정에서 동일한 내부 상태(Symbol, FontSize)를 가진 CharacterFlyweight 객체는 재사용됨으로써 메모리 사용을 최적화한다.