해당 글은 Tstory 블로그 Dev inpa에 출처를 두고 있습니다.

이번 패턴은 Flyweight 패턴에 관한 글이다.
나는 살면서 처음 들어보는 생소한 패턴이다.

Flyweight?

패턴의 이름을 말 그대로 직역하면 경량 패턴이다.
정의도 이름을 따라가는데, 아래와 같이 간단하게 정의할 수 있겠다.

객체의 생성을 줄여 메모리를 경량화하기 위한 패턴!

말 그대로 객체를 경량화 시킬 수 있는 패턴이다.
아래로 예시를 보며 좀 더 자세하게 공부해보도록 하자.

사용 이유

예를 들어 마인크래프트 게임을 한 번 생각해보자.
게임 내에서 어떤 필드가 주어지고, 해당 필드에 나무가 생성이 된다.
이 때 나무들은 각자 특징을 가질 수 있으며, 모두 하나의 객체로 취급된다.

아래와 같이 나무 객체, 지형 객체, 그리고 전체 메모리가 있다고 가정하자.
각각의 객체를 자바 코드로 구현한 것이다.

스크린샷 2024-01-20 오후 6 36 18
스크린샷 2024-01-20 오후 6 36 35
스크린샷 2024-01-20 오후 6 36 49

이 코드들 기반으로 각 특징 별 나무를 5그루씩 심어보도록 하겠다.
아래와 같이 메인 함수에서 나무를 심는 시뮬레이션이 가능하다.

스크린샷 2024-01-20 오후 6 38 04

미리 생성된 10000 크기의 지형에 나무를 5그루씩 심었다.
나무를 심을 때 마다 객체가 생성되므로, 총 15그루의 나무를 심었다.
따라서 총 사용된 메모리는 100 * 15 = 1500MB가 된다.

사실 생각해보면, 나무들의 특성인 mesh, texture은 어떤가?
같은 특징의 나무라면 분명히 동일한 mesh, texture를 가지게 된다.
즉, 같은 특징의 나무는 위치만 다르지 동일한 특성을 갖는다는 것이다!
그럼 사실상 우리는 메모리를 낭비하고 있다고 볼 수 있다.

이럴 때 사용할 수 있는 패턴이 바로 Flyweight 패턴!

구현

객체들 간에 중복되는 특징인 mesh, texture을 하나의 클래스로 빼낸다.
이 클래스를 우리는 Flyweight 클래스라고 칭하겠다.
또한, 공통된 부분은 변경될 일이 없으므로 final로 선언한다.

스크린샷 2024-01-20 오후 11 57 03

공통된 부분을 빼내었으면 이제 해당 클래스를 생산할 공장이 필요하다.
그냥 해당 객체를 생성하는 것이 아닌 유일한 객체를 생성하기 위함이다.
정적 팩토리 메서드를 이용해서 유일한 객체를 반환할 수 있다.

스크린샷 2024-01-21 오전 1 22 47

위와 같이 정적 팩토리 메서드를 통해 객체를 생산함을 볼 수 있다.
메서드의 형태가 Singleton과 굉장히 유사함을 확인할수 있다.
사실 Singleton 또한 정적 팩토리 메서드가 사용된다.

이제 위와 같이 구현했다면, Tree 객체를 직접 생성하지 않아도 된다.
아래와 같이 Flyweight 객체를 선언하여 객체를 생성하도록 한다.
해당 객체는 Cache에 저장되며, 이후 동일한 특성의 객체 생성이 요청되면,
기존의 Cache를 확인하여 해당 특성의 객체를 반환해준다.

스크린샷 2024-01-21 오전 1 52 54

그 결과 실제 메인 함수에서 특성 별 나무가 아래와 같이 생성된다.

스크린샷 2024-01-21 오전 2 45 50

특성 별로 처음 나무를 생성할 때만 객체가 생생됨을 확인할 수 있다.
또한 메모리의 사용량이 420MB로 크게 줄었음을 확인할 수 있다.
Flyweight 객체 90MB에 좌표 객체 10MB가 5개 생성되어,
(90 + 10 * 5) * 3의 계산으로 420MB가 된 것이다.

이렇게 공통 요소가 있어 반복 객체 생성을 할 필요가 없는 경우에,
경량 객체를 선언하여 한 번만 객체를 생성하도록 할 수 있는 패턴이다.

주의할 점

주의할 점은 위의 코드에서 구현했던 Cache이다.
우리가 직접 HashMap을 선언해서 인스턴스를 Cache로서 관리하는 것인데,
JVMGC(Garbage Collection)은 관리되는 인스턴스는 삭제하지 않는다.
즉, Cache는 더 이상 사용하지 않아도 GC가 해제하지 못한다.
따라서 더 이상 참조할 필요가 없는 객체라면, 직접 메모리를 해제해주어야 한다.