티스토리 뷰

플라이웨이트 패턴이란?

플라이웨이트 패턴은 재사용 가능한 객체 인스턴스를 공유시켜 메모리 사용량을 최소화하는 패턴이다. 자주 변하는 속성(extrinsit)과 변하지 않는 속성(intrinsit)을 분리하고 변하지 않는 속성을 캐시하여 재사용해 메모리 사용을 줄이는 방식으로, 동일하거나 유사한 객체들 사이에 가능한 많은 데이터를 서로 공유하여 사용하도록 해 최적화를 노리는 경량 패턴이다. 

intrinsic vs extrinsic 
intrinsic란 '고유한, 본질적인' 이라는 의미를 가진다. 즉, 인스턴스가 어떠한 상황에서도 변하지 않는 정보를 말하며 그래서 값이 고정되어 있기에 충분히 언제 어디서 공유해도 문제가 없게 된다. 따라서, intrinsic한 객체는 장소나 상황에 의존하지 않기 때문에 값이 고정되어 공유할 수 있는 객체를 뜻한다. 

extrinsic이란 '외적인, 비본질적인' 이라는 의미를 가진다. 즉, 인스턴스를 두는 장소나 상황에 따라서 변화하는 정보를 말하고, 그래서 값이 언제 어디서 변화할지 모르기 때문에 이를 캐시해서 공유할수는 없다. 따라서, extrinsic한 객체는 장소나 상황에 의존하기 때문에 매번 값이 바뀌어 공유할 수 없는 객체를 뜻한다.

플라이웨이트 패턴 사용법 (팩토리 방식)

    • Flyweight : 경량 객체를 묶는 인터페이스.
    • ConcreteFlyweight : 공유 가능하여 재사용되는 객체 (intrinsic state)
    • UnsahredConcreteFlyweight : 공유 불가능한 객체 (extrinsic state)
    • FlyweightFactory : 경량 객체를 만드는 공장 역할과 캐시 역할을 겸비하는 Flyweight 객체 관리 클래스
      • GetFlyweight() 메서드는 팩토리 메서드 역할을 한다고 보면 된다.
      • 만일 객체가 메모리에 존재하면 그대로 가져와 반환하고, 없다면 새로 생성해 반환한다
    • Client : 클라이언트는 FlyweightFactory를 통해 Flyweight 타입의 객체를 얻어 사용한다.

 

// ConcreteFlyweight - 플라이웨이트 객체는 불변성을 가져야한다. 변경되면 모든 것에 영향을 주기 때문이다.
final 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;
    }
}

// UnsahredConcreteFlyweight
class Tree {
    // 죄표값과 나무 모델 참조 객체 크기를 합친 사이즈
    long objSize = 10; // 10MB

    // 위치 변수
    double position_x;
    double position_y;

    // 나무 모델
    TreeModel model;

    public Tree(TreeModel model, double position_x, double position_y) {
        this.model = model;
        this.position_x = position_x;
        this.position_y = position_y;

        // 나무 객체를 생성하였으니 메모리 사용 크기 증가
        Memory.size +=  this.objSize;
    }
}

 

// FlyweightFactory
class TreeModelFactory {
    // Flyweight Pool - TreeModel 객체들을 Map으로 등록하여 캐싱
    private static final Map<String, TreeModel> cache = new HashMap<>(); // static final 이라 Thread-Safe 함

    // static factory method
    public static TreeModel getInstance(String key) {
        // 만약 캐시 되어 있다면
        if(cache.containsKey(key)) {
            return cache.get(key); // 그대로 가져와 반환
        } else {
            // 캐시 되어있지 않으면 나무 모델 객체를 새로 생성하고 반환
            TreeModel model = new TreeModel(
                    key,
                    new Object(),
                    new Object()
            );
            System.out.println("-- 나무 모델 객체 새로 생성 완료 --");

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

            return model;
        }
    }
}
// Client
class Terrain {
    // 지형 타일 크기
    static final int CANVAS_SIZE = 10000;

    // 나무를 렌더릴
    public void render(String type, double position_x, double position_y) {
        // 1. 캐시 되어 있는 나무 모델 객체 가져오기
        TreeModel model = TreeModelFactory.getInstance(type);

        // 2. 재사용한 나무 모델 객체와 변화하는 속성인 좌표값으로 나무 생성
        Tree tree = new Tree(model, position_x, position_y);

        System.out.println("x:" + tree.position_x + " y:" + tree.position_y + " 위치에 " + type + " 나무 생성 완료");
    }
}

패턴 사용 시기

  • 애플리케이션에 의해 생성되는 객체의 수가 많아 저장 비용이 높아질 때
  • 응용 프로그램이 많은 객체를 필요로 할 때
  • Extrinsic state를 제외하면 대부분의 객체들이 상대적으로 소수의 공유 객체로 대체될 수 있을 때
  • 응용 프로그램이 객체들을 서로 구분할 필요가 없을 때

패턴 장점

  • 애플리케이션에서 사용하는 메모리를 줄일 수 있다.
  • 프로그램 속도를 개선 할수 있다.
    • new로 인스턴스화를 하면 데이터가 생성되고 메모리에 적재 되는 미량의 시간이 걸리게 된다.
    • 객체를 공유하면 인스턴스를 가져오기만 하면 되기 때문에 메모리 뿐만 아니라 속도도 향상시킬 수 있게 되는 것이다.

패턴 단점

  • 객체의 상태를 내재 상태와 외재 상태로 분리 관리해야 하므로 설계가 복잡해질 수 있습니다.
  • 외재 상태를 관리하는 데 추가적인 계산 비용이나 시간이 들 수 있습니다. 특히, 외재 상태가 계산에 의존하는 경우 그 비용이 증가할 수 있습니다.
  • 상태를 적절히 관리하고, 객체의 생성과 사용을 정확히 제어해야 하는 복잡성 때문에 구현이 어려울 수 있습니다.

참고

 

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

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

inpa.tistory.com