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

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

by 왹져박사 2023. 4. 15.
728x90

메모리와 직접적으로 연관이 있는 플라이웨이트 패턴. 

팀원들과 디자인패턴 스터디를 시작하면서, 우선적으로 게임에 주로 활용되는 패턴을 찾아보았다. 

그리고 현재 진행 중인 프로젝트 <꿈의 왕국 : 영원한 보금자리>에 활용할만한 디자인패턴을 찾았다. 

본인은 프로젝트에서 디자인과 리소스 관련 역할이 다른 팀원들에 비해 큰 편이라고 생각하였으며, 

플라이웨이트를 잘 활용한다면 프로젝트의 메모리를 잘 관리할 수 있을 것이라 생각하였다. 

특히, 레퍼런스게임 중 하나인 <프로즌시티>를 직접 플레이하며 가장 놀랐던 점이

다른 방치형 게임들에 비하여 발열이 거의 없다!라는 점이었다. 방치형 게임을 하다 보면 밤에 깜빡하고 켜두고 잠들 때가 종종 있는데, 다른 게임들은 폭발하는 거 아닌가 싶을 정도로 발열이 굉장히 심했던 기억이 있다. 하지만 프로즌시티는 살짝 따뜻한 정도로만 느껴졌다. 

게임 내에서의 오브젝트와 이미지를 적절히 배치한 부분도 보였지만, 플라이웨이트를 보고 이 패턴을 적절히 사용한 방법도 그중 하나가 아닐까? 하는 생각이 들었다. 

 

플라이웨이트(Flyweight)

플라이웨이트 패턴은 객체들을 반복되는 동일한  객체인 공유 가능 상태(불변)개체마다 변경되는 공유 불가능 상태(고유한 상태)로 구분하는 것이 특징이다. 공유 가능한 상태의 객체를 만들어 객체의 생성을 최소화시켜 메모리 사용량을 줄이는 것이 핵심이다. 그렇기 때문에, 대규모 데이터를 다룰수록 더욱 효율적으로 메모리 관리를 할 수 있다. 

이를 게임 개발에서는 리소스 관리, 게임 오브젝트 관리(메모리 최적화), 이벤트 시스템, 네트워크 통신 등에 활용할 수 있다고 한다. 

이러한 플라이웨이트 패턴은 객체의 공유의 적절한 시점과 범위의 고려, 상태의 결정이 매우 중요하다. 프로젝트의 구조에 맞게 적절하게 활용한다면 메모리 사용을 최적화하고 성능을 향상시킬 수 있을 것이다. 

 

chatGPT에게 공유 가능 상태와 불가능 상태를 잘 결정하는 방법을 물어보았다. 요약하자면, 

1) 객체의 불변성 

: 공유되는 한 객체의 상태가 변경되면 다른 객체들에 영향을 미칠 수 있다. 

2) 객체의 빈도와 비용

 : 사용 빈도가 낮거나 생성 비용이 낮은 객체는 공유불가능한 상태로 나두는 것이 효율적일 수도 있다. 

3) 객체의 고유성

: 여러 객체가 동일한 상태를 공유할 수 있다면 공유 가능한 상태로 관리하는것이 효율적일 수 있다. 

4) 상태의 중요성과 보안성

: 객체의 상태가 중요한 정보를 포함하거나 보안상의 이슈가 있는 경우, 공유 불가능한 상태로 관리하는 것이 안전할 수 있다. 

5) 시스템의 복잡성

: 시스템이 복잡한경우, 객체 간의 상태 공유가 복잡해질 수 있고 이를 관리하기 어려울 수 있다. 상태 공유를 최소화하고 상태를 적절하게 결정해야 함

6) 성능 요구사항

: 성능이 중요한 시스템일 경우 객체의 상태를 공유 가능한 상태로 관리하여 성능 향상을 도모할 수 있다. 

 

 

예시

다음은 Unity 프로젝트에서 C# 언어를 사용하여 chatGPT가 보여준 간단한 예시이다. 

using System.Collections.Generic;
using UnityEngine;

// 플라이웨이트 패턴의 공유 가능한 상태를 나타내는 클래스
class Flyweight
{
    public string SharedData { get; set; }
    // 추가적인 공유 가능한 상태 변수들
}

// 플라이웨이트 패턴의 클라이언트
class Client
{
    public void Operation(string key)
    {
        // Flyweight 팩토리를 통해 Flyweight 객체를 가져옴
        Flyweight flyweight = FlyweightFactory.GetFlyweight(key);

        // Flyweight 객체의 공유 가능한 상태에 접근하여 사용
        Debug.Log("Shared Data: " + flyweight.SharedData);
    }
}

// Flyweight 객체를 생성하고 관리하는 팩토리 클래스
class FlyweightFactory
{
    private static Dictionary<string, Flyweight> flyweights = new Dictionary<string, Flyweight>();

    public static Flyweight GetFlyweight(string key)
    {
        if (flyweights.ContainsKey(key))
        {
            // 이미 해당 키의 Flyweight 객체가 존재하면 그 객체를 반환
            return flyweights[key];
        }
        else
        {
            // 해당 키의 Flyweight 객체가 없으면 새로 생성하여 반환
            Flyweight flyweight = new Flyweight();
            flyweight.SharedData = "Shared Data for Key: " + key;
            flyweights.Add(key, flyweight);
            return flyweight;
        }
    }
}

// 예시의 사용
public class Example : MonoBehaviour
{
    void Start()
    {
        // 클라이언트 객체 생성
        Client client = new Client();

        // 클라이언트가 Flyweight 패턴을 사용하여 작업 수행
        client.Operation("Key1");
        client.Operation("Key2");
        client.Operation("Key1");
    }
}

플라이웨이트 패턴을 사용하여 공유 가능한 상태를 가진 Flyweight 클래스를 구현하고, 클라이언트 객체가 Flyweight 팩토리를 통해 Flyweight 객체를 가져와 사용하는 방식이다. 

 

이렇게 효율적일것만 같은 플라이웨이트 패턴도 단점이 있다. 

단점

1) 상태의 구분

: 공유 가능한 상태와 불가능한 상태를 명확하게 구분해야 한다. 공유 가능한 상태를 효과적으로 공유하기 위해서는 상태를 변경되지 않도록 만들어야 하고, 상태를 변경해야 한다면 새로운 객체를 생성하여 대체한다. 이런 상태 변경은 복제와 관련된 비용이 추가될 수 있다. 

2) 복잡성 증가

: 추가적인 객체들을 생성, 관리하기 위한 구성요소가 필요하다. 이로 인해 시스템 복잡성이 증가할 수 있다. 

또한 팩토리에 대한 동기화 처리가 필요한 경우에도 추가적인 복잡성이 증가할 수 있다. 

3) 상태의 공유 가능성 제한

: 모든 객체에 대해 적용할 수 없을 수 있다. 

4) 객체 식별의 복잡성

: 객체의 식별을 Dictionary의 key를 통하여 관리한다. 객체를 검색하기 위해 키를 사용하는 과정에서 추가적인 복잡성이 발생할 수 있다. 

 

 

이제 이러한 고려요소들을 알아보고 예시를 통해 우선 진행적인 프로토타입에 적용하여 프로젝트에 잘 활용할만한 패턴인지 알아보려고 한다. 디자인 패턴을 공부할수록 신기하고 재미있다. 패턴 예시들을 하나하나 보면 사실 이미 배운 부분들로 구성되어 있다. 하지만 이를 적절한곳에 사용하고 배치하여 잘 활용할만한 하나의 서식, 패턴이 만들어진다. '디자인 패턴'이라는 명칭에서도 예상가긴 했지만 본 전공인 패션디자인에서 배우는 패턴들과 굉장히 비슷한 느낌이 들었다. 기본적인 패턴이 있지만 이를 내가 프로젝트에서 얼마나 적절하게 사용하도 다듬는지에 따라 결과물이 달라질 것이라고 생각하면 굉장히 흥미롭고 재미있다. 앞으로 알아볼 디자인 패턴들도 기대가 된다. 

 

728x90