[디자인 패턴] Chapter 20. Flyweight 패턴
01. Flyweight 패턴
- 복싱에서의 ‘플라이급’을 의미한다 ⇒ 매우 가벼움
- 가볍다 == 메모리의 사용량이 적다
- new Something()이 실행되면, Something 클래스의 인스턴스를 보관하기 위한 메모리가 할당된다
- Flyweight 패턴의 아이디어
- 인스턴스를 가능한 한 공유시켜서, 쓸데없이 new를 하지 않도록 한다
- 인스턴스가 필요할 때 마다 new를 하는 것이 아니라, 이미 만들어져 있는 인스턴스를 이용할 수 있으면 공유하자
02. 예제 프로그램
- ‘큰 문자’를 출력하는 프로그램
이름 | 해설 |
---|---|
BigChar | ‘큰 문자’를 나타내는 클래스 |
BigCharFactory | BigChar의 인스턴스를 공유하면서 생성하는 클래스 |
BigString | BigChar를 모아서 만든 ‘큰 문자열’을 나타내는 클래스 |
Main | 동작 테스트용 클래스 |
주요 클래스
- BigChar
- ‘큰 문자’를 나타내는 클래스
- 파일로부터 큰 문자의 텍스트를 읽어 메모리에 저장하고, print 메소드에서 그것을 표시한다
- 큰 문자는 메모리를 많이 차지하므로, BigChar의 인스턴스를 공유하자
- BigCharFactory
- BigChar 클래스의 인스턴스를 만든다
- 같은 문자에 대응하는 BigChar 클래스의 인스턴스가 이미 만들어져 있는 경우에는, 이것을 이용하고 새로운 인스턴스는 만들지 않는다
- 지금까지 만들어진 BigChar 클래스의 인스턴스는 모두 pool이라는 필드에 보관함(HashMap을 이용)
BigChar 클래스
- ‘큰 문자’를 나타내는 클래스
- 생성자
- 인수로 제공된 문자의 ‘큰 문자’ 버전을 작성한다
- 작성된 문자열은 fontdata에 저장함
- 예를 들어, 생성자의 인수로 ‘3’이 주어지면, 다음과 같은 문자열이 fontdata에 저장됨 ⇐ 이 data는 big3.txt에서 읽어온다
- 파일로부터 한 라인 씩 읽어서, “\n”과 함께 buf에 추가한다
- but.toString()을 이용해서 문자열로 바꾼다음 fontdata에 할당한다
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BigChar {
private char charname;
private String fontdata;
public BigChar(char charname) {
this.charname = charname;
try {
BufferedReader reader = new BufferedReader(
new FileReader("big" + charname + ".txt")
);
String line;
StringBuffer buf = new StringBuffer();
while ((line = reader.readLine()) != null) {
buf.append(line);
buf.append("\n");
}
reader.close();
this.fontdata = buf.toString();
} catch (IOException e) {
this.fontdata = charname + "?";
}
}
public void print() {
System.out.print(fontdata);
}
}
BigCharFactory 클래스
- BigChar의 인스턴스를 생성하는 공장
- 여기서 인스턴스의 공유가 실현된다
- pool 필드:
- 지금까지 만든 BigChar의 인스턴스들을 모아서 관리한다
- java.util.HashMap을 이용함
- “문자열(key) → 인스턴스(value)” 대응관계를 관리해 주는 클래스
- put(key, value)를 이용하여 문자열에 하나의 인스턴스를 대응해서 넣음
- get(key)를 이용하여, 문자열에 대응하는 인스턴스를 얻어옴
- Singleton 패턴으로 구현됨
- BigCharFactory의 인스턴스는 하나만 존재하면 되므로
- singleton 필드
- 생성자는 private으로 선언
- getInstance() 메소드 : BigCharFactory의 인스턴스를 얻기 위한 메소드
- getBigChar()
- Flyweight 패턴의 중심이 되는 메소드
- 인수로 제공된 문자에 대응하는 BigChar 인스턴스를 만든다
- 이미 같은 문자에 대응하는 인스턴스가 존재하면 새로운 인스턴스를 만들지 않고, 이전에 만든 인스턴스를 반환한다
import java.util.HashMap;
public class BigCharFactory {
private HashMap pool = new HashMap();
private static BigCharFactory singleton = new BigCharFactory();
private BigCharFactory() {
}
public static BigCharFactory getInstance() {
return singleton;
}
public synchronized BigChar getBigChar(char charname) {
BigChar bc = (BigChar)pool.get("" + charname);
if (bc == null) {
bc = new BigChar(charname);
pool.put("" + charname, bc);
}
return bc;
}
}
BigString 클래스
- BigChar들을 모은 ‘큰 문자열’ 클래스
- 예 : “1212123”이라는 문자열에 대응하는 BighString 인스턴스
- 필드 bigchars 배열의 각 원소는 BigCharFactory로부터 BigChar를 얻어서 참조한다
- BigCharFactory는, 이미 생성된 BigChar인 경우에는 기존의 BigChar 인스턴스를 반환하므로, bigchars 배열의 각 원소는 같은 문자에 대해서는 BigChar 객체를 공유한다
- (참고) 매번 새로운 인스턴스를 생성하는 경우
- BigCharFactory는, 이미 생성된 BigChar인 경우에는 기존의 BigChar 인스턴스를 반환하므로, bigchars 배열의 각 원소는 같은 문자에 대해서는 BigChar 객체를 공유한다
public class BigString {
private BigChar[] bigchars;
public BigString(String string) {
bigchars = new BigChar[string.length()];
BigCharFactory factory = BigCharFactory.getInstance();
for (int i = 0; i < bigchars.length; i++) {
bigchars[i] = factory.getBigChar(string.charAt(i));
}
}
public void print() {
for (int i = 0; i < bigchars.length; i++) {
bigchars[i].print();
}
}
}
Main 클래스
- 명령행 인자로 들어온 문자열을 실 매개변수로 해서, BigString() 객체를 생성한다
public class Main {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage : java Main digits");
System.out.println("Example : java Main 1212123");
System.exit(0);
}
BigString bs = new BigString(args[0]);
bs.print();
}
}
03. 등장 역할
- Flyweight(플라이급)의 역할
- 평소대로 취급하면 프로그램이 무거워져서(메모리를 많이 차지해서) 공유하는게 훨씬 좋은 역할
- 예제에서는, BigChar 클래스가 해당됨
- FlyweightFactory(플라이급의 공장)의 역할
- Flyweight 역할을 만드는 공장의 역할
- 공장을 사용해서 Flyweight 역할을 만들면, 인스턴스가 공유된다
- 예제에서는, BigCharFactory 클래스가 해당됨
- Client(의뢰자)의 역할
- FlyweightFactory를 사용해서 Flyweight 역할을 만들어내서 이용하는 역할
- 예제에서는, BigString 클래스가 해당됨
04. 독자의 사고를 넓혀주는 힌트
- 여러 곳에 영향이 미친다
- Flywieght 패턴의 핵심은, 인스턴스를 ‘공유‘하는 것이다
- 주의해야 할 점
- 공유하고 있는 것을 변경하면 여러 곳에 동시에 영향이 미친다
- 꼭 공유시켜야 할 정보를 Flyweight 역할에 맡겨라
- 예 : 예제 프로그램에, ‘채색된 큰 문자열’을 사용하고자 할 때 색깔 정보를 어느 클래스에 두어야 할까?
- 방법1 : BigChar에 두는 방법 ⇒ BigString 내의 공유되는 모든 BigChar 인스턴스는 동일한 색을 가지게 된다
- 방법2 : BigString에 두는 방법 ⇒ “몇 번째 문자는 무슨 색깔”이라는 정보를 BigString이 가지고 있으므로, 공유되는 BigChar 인스턴스라도 다른 색깔로 표현될 수 있다
- 항상 어떤 정보를 공유시킬지 신중히 생각해야 한다
- 예 : 예제 프로그램에, ‘채색된 큰 문자열’을 사용하고자 할 때 색깔 정보를 어느 클래스에 두어야 할까?
- instrinsic(본질적인, 내재하는)과 extrinsic(부대적인, 비본질적인)
- instrinsic한 정보 : 공유되어야 하는 정보
- 상태에 의존하지 않는 정보
- 예 : BigChar의 폰트 데이터
- extrinsic한 정보 : 공유시키지 말아야 할 정보
- 상태에 따라 바뀌는 정보를 의미함
- BigChar의 인스턴스가 BigString의 몇 번째 문자인가라는 정보는 BigChar가 위치하는 장소에 따라 변하기 때문에, BigChar에게 부여할 수 없다
-
종류 설명 instrinsic한 정보 장소나 상황에 의존하지 않기 때문에 공유할 수 있습니다 extrinsic한 정보 장소나 상황에 의존하기 때문에 공유할 수 없습니다
- instrinsic한 정보 : 공유되어야 하는 정보
- 관리되고 있는 인스턴스는 쓰레기수집(Garbage collection)이 되지 않는다
- BigChar 인스턴스는 HashMap에 의해서 관리된다. 따라서, 절대 쓰레기 수집이 되지 않는다
- 쓰레기 수집
- 자바는 new 연산자에 의해 객체를 생성하고, 메모리를 확보한다
- 객체가 많아지면, 메모리가 부족해지고, 자바 가상 기계는 쓰레기 수집이라는 처리를 시작한다
- 메모리 공간을 조사하며, 사용되지 않는 인스턴스를 제거하고 메모리의 빈 영역을 늘인다
- 메모리 이외의 리소스
- 프로그램에서의 리소스
- 메모리 공간
- 시간
- Flyweight 패턴을 사용하면, new 횟수를 줄이므로 프로그램의 속도를 높일 수 있다
- 프로그램에서의 리소스
05. 관련 패턴
- Proxy 패턴
- Composite 패턴
- Singleton 패턴
06. 요약
- 메모리의 소비를 적게 하기 위해, 인스턴스를 공유시키는 Flyweight 패턴
연습 문제
- 20-1
- BigString(String string, Boolean shared)
- shared가 true일 때는 BigChar를 공유하고
- shared가 false일 때는 BigChar를 공유하지 않음
- BigString(String string, Boolean shared)
- 20-2
- BigChar 인스턴스를 공유한 경우와 공유하지 않은 경우의 메모리 소비량을 비교하라
- 메모리 소비량을 얻는 방법
- 20-3
- BigCharFactory의 getBigChar()가 synchronized로 선언된 이유는?
댓글남기기