자바 디자인 패턴 용어 사전
구조 패턴플라이웨이트 · 경량 패턴

Flyweight

변하지 않는 상태를 공유해 객체 수를 줄이는 패턴. 내재 상태와 외재 상태를 나눈다.

변하지 않는 상태를 공유해 메모리를 절약하는 패턴.

두 상태를 나눈다

  • 내재 상태(intrinsic) — 객체마다 같다 → 공유한다

    • 예) 글자의 폰트·글리프 모양, 나무의 종류·텍스처
  • 외재 상태(extrinsic) — 객체마다 다르다 → 밖에서 넘긴다

    • 예) 글자의 위치, 나무의 좌표
// 내재 상태만 담은 공유 객체
class TreeType {
    private final String name; private final Texture texture;   // 무겁다
}

class TreeFactory {
    private static final Map<String, TreeType> cache = new HashMap<>();
    static TreeType get(String name) {
        return cache.computeIfAbsent(name, TreeType::new);       // 같은 종류는 재사용
    }
}

// 외재 상태는 사용 시점에 넘긴다
class Tree {
    private final int x, y;                 // 가볍다
    private final TreeType type;            // 공유 참조
    void draw() { type.draw(x, y); }
}
숲에 나무 100만 그루, 종류는 10가지라면
  Flyweight 없이  무거운 객체 1,000,000개
  Flyweight 적용  무거운 객체 10개 + 가벼운 객체 1,000,000개

실무에서 — 이미 쓰고 있다

Integer.valueOf(127)      // -128~127 캐시 — 같은 인스턴스를 돌려준다
String 리터럴 풀           // 같은 리터럴은 하나의 인스턴스
Boolean.valueOf(true)

이 때문에 Integer a = 127, b = 127; a == btrue 인데 128 이면 false 가 된다. 자바의 대표적 함정이 사실 Flyweight의 결과다.

대가

  • 공유 객체는 반드시 불변이어야 한다 (하나가 바뀌면 전부 바뀐다)
  • 외재 상태를 매번 넘겨야 해 코드가 번거로워진다
  • 객체 수가 적으면 캐시 관리 비용만 남는다

면접 함정

  • "캐시와 같다" → 캐시는 조회 결과 재사용, Flyweight는 상태 분리를 통한 공유가 설계 의도다.
  • 공유 객체에 가변 필드 → 한 곳의 변경이 전부에 퍼진다.

캐시와 무엇이 다른가

  • 캐시 — 조회 결과 저장 — 성능 최적화. 인스턴스가 여러 개여도 된다

  • Flyweight — 내재/외재 상태를 '나누는 설계'. 같은 내재 상태 = 같은 인스턴스

  • 캐시는 구현 수단이고 Flyweight 는 설계 결정이다

자바 문자열 풀

String a = "hello", b = "hello";
a == b                      // true — 풀에서 같은 인스턴스
String c = new String("hello");
a == c                      // false
c.intern() == a             // true

언제 쓰지 말아야 하나

  • 객체 수가 적다 → 캐시 자료구조 비용만 남는다

  • 내재 상태가 가볍다 → 공유해서 아낄 게 없다

  • 외재 상태 전달이 지나치게 번거로워진다

  • 측정 먼저다. "객체가 많으니 Flyweight" 는 근거가 아니다

동시성 주의

공유 객체는 여러 스레드가 동시에 참조하므로 반드시 불변이어야 하고, 팩토리의 캐시 맵도 스레드 안전해야 한다(ConcurrentHashMap).

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 구조 패턴 ② — 감추고, 공유하고, 대신한다