You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 문제 1: 수많은 객체로 메모리 부족publicclassGame {
privateList<Tree> trees = newArrayList<>();
publicvoidcreateForest() {
for (inti = 0; i < 1000000; i++) {
// 100만 그루의 나무 생성!Treetree = newTree(
"Oak", // 나무 종류"Green", // 색상"oak_texture.png"// 텍스처
);
tree.setPosition(random(), random());
trees.add(tree);
}
// 100만 개 객체 = 수백 MB 메모리!
}
}
publicclassTree {
privateStringname; // 모두 동일privateStringcolor; // 모두 동일privateStringtexture; // 모두 동일privateintx; // 다름privateinty; // 다름// 같은 데이터를 100만 번 복사!
}
// 문제 2: 텍스트 에디터의 문자 객체publicclassTextEditor {
privateList<Character> characters = newArrayList<>();
publicvoidaddText(Stringtext) {
for (charc : text.toCharArray()) {
// 각 문자마다 객체 생성Charactercharacter = newCharacter(
c, // 글자"Arial", // 폰트 (대부분 동일)12, // 크기 (대부분 동일)"Black"// 색상 (대부분 동일)
);
characters.add(character);
}
}
// "Hello"를 100번 쓰면 500개 'e' 객체!// 같은 'e'를 왜 500번 만들까?
}
// 문제 3: 아이콘 캐시 없음publicclassFileExplorer {
publicvoiddisplayFiles(List<File> files) {
for (Filefile : files) {
Iconicon;
if (file.getExtension().equals("pdf")) {
icon = newPDFIcon(); // 매번 생성!
} elseif (file.getExtension().equals("jpg")) {
icon = newJPGIcon(); // 매번 생성!
}
display(icon, file.getName());
}
// PDF 100개 = PDF 아이콘 100개 생성!
}
}
⚡ 핵심 문제
메모리 낭비: 동일한 데이터를 반복 저장
성능 저하: 불필요한 객체 생성
GC 부담: 많은 객체로 인한 GC 오버헤드
확장성 제약: 메모리 한계로 객체 수 제한
2. 패턴 정의
📖 정의
공유 가능한 객체를 통해 메모리 사용량을 줄이는 패턴. 많은 수의 유사한 객체를 효율적으로 지원한다.
🎯 목적
메모리 절약: 공유로 메모리 사용 최소화
성능 향상: 객체 생성 비용 절감
확장성: 더 많은 객체 생성 가능
캐싱: 재사용 가능한 객체 관리
💡 핵심 아이디어
// Before: 100만 개 Tree 객체for (inti = 0; i < 1000000; i++) {
newTree("Oak", "Green", "texture.png", x, y);
}
// 100만 개 x 1KB = 1GB// After: 1개 TreeType 객체 공유TreeTypeoakType = factory.getTreeType("Oak", "Green", "texture.png");
for (inti = 0; i < 1000000; i++) {
newTree(oakType, x, y); // 참조만 저장
}
// 1개 x 1KB + (100만 개 x 8bytes) = 8MB
📊 Intrinsic vs Extrinsic State
구분
설명
공유 가능?
예시
Intrinsic State
객체 내부 상태 (불변)
✅ 공유
나무 종류, 색상, 텍스처
Extrinsic State
객체 외부 상태 (가변)
❌ 개별
나무 위치 (x, y)
3. 구조와 구성요소
📊 UML 다이어그램
┌─────────────────┐
│FlyweightFactory │ ← 공유 객체 관리
├─────────────────┤
│ - flyweights │
│ + getFlyweight()│
└─────────────────┘
│
│ creates & manages
▼
┌─────────────────┐
│ Flyweight │ ← 공유 객체
├─────────────────┤
│ - intrinsic │ ← 내부 상태 (공유)
│ + operation( │
│ extrinsic) │ ← 외부 상태 (개별)
└─────────────────┘
🔧 구성요소
요소
역할
예시
Flyweight
공유 가능한 객체
TreeType
FlyweightFactory
Flyweight 생성/관리
TreeFactory
Client
외부 상태 유지
Tree (x, y 좌표)
4. 구현 방법
기본 구현: 게임 포레스트 ⭐⭐⭐
/** * Flyweight: 나무 타입 (공유 가능) */publicclassTreeType {
privateStringname;
privateStringcolor;
privateStringtexture;
publicTreeType(Stringname, Stringcolor, Stringtexture) {
this.name = name;
this.color = color;
this.texture = texture;
System.out.println("🌲 TreeType 생성: " + name + " (" + color + ")");
}
publicvoiddraw(intx, inty) {
System.out.println(" Drawing " + name + " tree at (" + x + "," + y +
") with " + color + " color");
}
publicStringgetName() {
returnname;
}
}
/** * FlyweightFactory: 나무 타입 팩토리 */publicclassTreeFactory {
privatestaticMap<String, TreeType> treeTypes = newHashMap<>();
publicstaticTreeTypegetTreeType(Stringname, Stringcolor, Stringtexture) {
// 키 생성Stringkey = name + "-" + color + "-" + texture;
// 캐시 확인TreeTypetype = treeTypes.get(key);
if (type == null) {
// 없으면 생성 후 캐시type = newTreeType(name, color, texture);
treeTypes.put(key, type);
System.out.println("✅ 캐시에 저장: " + key);
} else {
System.out.println("♻️ 캐시에서 재사용: " + key);
}
returntype;
}
publicstaticintgetCacheSize() {
returntreeTypes.size();
}
}
/** * Client: 나무 (외부 상태 포함) */publicclassTree {
privateintx;
privateinty;
privateTreeTypetype; // 공유 객체 참조publicTree(intx, inty, TreeTypetype) {
this.x = x;
this.y = y;
this.type = type;
}
publicvoiddraw() {
type.draw(x, y);
}
}
/** * 숲 시뮬레이션 */publicclassForest {
privateList<Tree> trees = newArrayList<>();
publicvoidplantTree(intx, inty, Stringname, Stringcolor, Stringtexture) {
TreeTypetype = TreeFactory.getTreeType(name, color, texture);
Treetree = newTree(x, y, type);
trees.add(tree);
}
publicvoiddraw() {
System.out.println("\n=== 숲 그리기 ===");
for (Treetree : trees) {
tree.draw();
}
}
publicvoidprintStats() {
System.out.println("\n=== 메모리 통계 ===");
System.out.println("총 나무 수: " + trees.size());
System.out.println("TreeType 캐시 크기: " + TreeFactory.getCacheSize());
// 메모리 절약 계산longwithoutFlyweight = trees.size() * 1024; // 1KB per treelongwithFlyweight = TreeFactory.getCacheSize() * 1024 + trees.size() * 16;
// 16 bytes per Tree (x, y, reference)System.out.println("Flyweight 없이: ~" + withoutFlyweight / 1024 + " KB");
System.out.println("Flyweight 사용: ~" + withFlyweight / 1024 + " KB");
System.out.println("절약: ~" + (withoutFlyweight - withFlyweight) / 1024 + " KB");
}
}
/** * 사용 예제 */publicclassFlyweightExample {
publicstaticvoidmain(String[] args) {
Forestforest = newForest();
System.out.println("=== 나무 심기 ===");
// 같은 종류의 나무를 여러 번 심기forest.plantTree(10, 20, "Oak", "Green", "oak.png");
forest.plantTree(15, 25, "Oak", "Green", "oak.png"); // 재사용!forest.plantTree(20, 30, "Oak", "Green", "oak.png"); // 재사용!forest.plantTree(30, 40, "Pine", "Dark Green", "pine.png");
forest.plantTree(35, 45, "Pine", "Dark Green", "pine.png"); // 재사용!forest.plantTree(50, 60, "Birch", "White", "birch.png");
// 숲 그리기forest.draw();
// 통계 출력forest.printStats();
}
}
실행 결과:
=== 나무 심기 ===
🌲 TreeType 생성: Oak (Green)
✅ 캐시에 저장: Oak-Green-oak.png
🌲 TreeType 생성: Oak (Green)
♻️ 캐시에서 재사용: Oak-Green-oak.png
🌲 TreeType 생성: Oak (Green)
♻️ 캐시에서 재사용: Oak-Green-oak.png
🌲 TreeType 생성: Pine (Dark Green)
✅ 캐시에 저장: Pine-Dark Green-pine.png
🌲 TreeType 생성: Pine (Dark Green)
♻️ 캐시에서 재사용: Pine-Dark Green-pine.png
🌲 TreeType 생성: Birch (White)
✅ 캐시에 저장: Birch-White-birch.png
=== 숲 그리기 ===
Drawing Oak tree at (10,20) with Green color
Drawing Oak tree at (15,25) with Green color
Drawing Oak tree at (20,30) with Green color
Drawing Pine tree at (30,40) with Dark Green color
Drawing Pine tree at (35,45) with Dark Green color
Drawing Birch tree at (50,60) with White color
=== 메모리 통계 ===
총 나무 수: 6
TreeType 캐시 크기: 3
Flyweight 없이: ~6 KB
Flyweight 사용: ~3 KB
절약: ~3 KB