The Flyweight Pattern

    데이터를 표현하기 위한 작은 클래스의 인스턴스들을 많이 생성해야 할 경우가 있다.
종종, 만약 몇개의  파라미터에 대한 것만 제외하고 기본적으로 같은 같은 인스턴스들이다라는 것을 인식할 수 있다면 인스턴스를 만들 필요가 있는 서로 다른 클래스들의 수를 크게 줄일 수 있다. 만약 그러한 변수들을 클래스 인스턴스의 외부로 옮길 수 있고 메소드가 호출하는 부분에서 그것들을 건내 줄수 있다면 나뉘어진 인스턴스의 수는 크게 줄일 수 있다. 

    Flyweight 디자인 패턴은 그러한 클래스들을 조절하는 거에 대해서 방법을 제시한다. 인스턴스가 유일하도록 인스턴스의 고유한 데이터를 참조하고, 아규먼트로 전달되는 고유하지 않는 데이터를 참조하게 한다. Flyweight는 각각의 문자들이나 스크린사의 아이콘과 같은 작고, 자잘 자잘한 클래스들에 대해  어울린다.  예를 들어, 사람이나 데이터에 대해 각각 표현을 하는 폴더 윈도우에서 화면상의 아이콘의 시리즈를 추출한다고 하면 ,사람의 이름이나 화면상의 아이콘의 위치 를 기억하는 것에 대해 각각의 클래스 인스턴스를 만드는 것은 의미가 없다. 전형적으로 그러한 아이콘들은 몇몇 유사한 이미지들중의 하나이고,추출되는 위치는 어떤 경우에서든지 윈도우의 크기에 따라 동적으로 계산되어 진다. 

    디자인 패턴에서의 다른 예는 폰트에서 각각의 문자는 문자 클래스의 하나의 인스턴스로 표현되어지지만, 글자들이 스크린 상에서 그려지는 위치는 문자의 각각의 외양에 대한 것 보다는 각 문자의 하나의 인스턴스만 필요하게 하는 외부 데이터로서 유지된다.       

Discussion

    Flyweight들은 클래스의 인스턴스를 공유할 수 있다. 그것은 Singleton 클래스처럼 보일 수도 있지만 사실 적은 수의 인스턴스들이 있을 수 있다. 할당된 인스턴스의 수는 클래스 인스턴스가 필요할 때 결정되어야 하며, 이것은 Flyweight 클래스로 이루어 진다. 이 팩토리 클래스는 특별한 인스턴스가 생성되었는지 아닌지를 추적해야 하기 때문에 보통 싱글턴이다. 특수한 인스턴스가 이미 생성되어 있으면 새로운 인스턴스나 레퍼런스를 반환한다.    
    프로그램의 일부분이 Flyweight를 사용할 후보인지를 결정하기 위하여 클래스로부터 몇몇 데이터를 제거하고 그것을 외부의 변수로 만들어도 가능한가를 고려해 본다. 프로그램을 유지하는데 필요한 다른 클래스 인스턴스의 수를 크게 줄이는게 가능하도록 한다면 Flyweight 가 도울 수 있는 경우이다.  

Example Code

    우리가 하나의 조직에서 각 사람에 대해서 작은 폴더 아이콘 아래 이름을 가지는 작은 폴더 아이콘을 그리고 싶다고 해보자. 만약 이것이 큰 조직이라면 그러한 아이콘의 수는 커지게 되겠지만, 그것들은 실제적으로 작은 그래픽 이미지들이다. 우리가 두개의 아이콘을 가지고 있다고 하더라도 하는 선택되지 않은 거에 대한 것이고 다른 하나는 선택것에 대한 아이콘이다. 각 사람에 대한 아이콘 객체와 그 것들 자체의 좌표, 이름과 선택된 상태를 갖는 시스템은 자원의 낭비이다. 

    대신, 우리는 선택되거나 선택되지 않은 것을 나타내는 drawing 클래스를 반환하는 FolderFactory를 생성하겠지만, 이미 생성된 각각의 인스턴스에 대하여 추가적인  인스턴스 생성은 하지 않을 것이다. 
	
class FolderFactory {
	Folder unSelected, Selected;
	public FolderFactory() {
		Color brown = new Color(0x5f5f1c);
		Selected =  new Folder(brown);
		unSelected = new Folder(Color.yellow);
	}
	//-------------------------------  
	public Folder getFolder(boolean isSelected) {
		if (isSelected) 
			return Selected;
		else
			return unSelected;
	}
}
    더 이상의 인스턴스들이 존재할 수 있는 경우들에 대해서, factory는 이미 생성되었던 인스턴스에 대한 표를 유지할 수 있고 오직 테이블에서 이미 존재하지 않을 때에만 새로운 인스턴스를 생성한다. 

    그러나, Flyweight를 사용하는 것에 대한 유일한 것은 우리가 폴더 아이콘을 그릴 때 좌표와 이름을 넘겨주는 것이다. 이러한 좌표들은 폴더 객체를 공유하도록 하는 외부의 데이터이고 이 경우에 오직 두 개의 인스턴스만을 생성한다. 완성된 폴더 클래스는 아래의 코드 처럼 간단하게 폴더 인스턴스를 하나의 배경색과 다른  정한 폴더를 그릴 수 있는 public Draw 메소드를 가지고 생성할 수 있다. 
	
class Folder extends JPanel {
	private Color color;
	final int W = 50, H = 30;
	public Folder(Color c) {
	   color = c;
	}
	//-------------------------------  
	public void Draw(Graphics g, int tx, int ty, String name) {
		g.setColor(Color.black);            //outline
		g.drawRect(tx, ty, W, H);
		g.drawString(name, tx, ty + H+15);  //title
		
		g.setColor(color);                  //fill rectangle 
		g.fillRect(tx+1, ty+1, W-1, H-1);
		
		g.setColor(Color.lightGray);        //bend line   
		g.drawLine(tx+1, ty+H-5, tx+W-1, ty+H-5);
		
		g.setColor(Color.black);            //shadow lines
		g.drawLine(tx, ty+H+1, tx+W-1, ty+H+1);
		g.drawLine(tx+W+1, ty, tx+W+1, ty+H);
		
		g.setColor(Color.white);            //highlight lines
		g.drawLine(tx+1, ty+1, tx+W-1, ty+1);
		g.drawLine(tx+1, ty+1, tx+1, ty+H-1);  
	}
}
이와 같은 Flyweight 클래스를 사용하기 위해서, 메인 프로그램은 각 폴더의 위치를 계산해야 하고, 그리고 나서 좌표를 폴더 인스턴스에 건내 주어야 한다. 이것은 실제로 보다 일반적인 방법인데, 왜냐면 윈도우의 크기에 의존하는 서로 다른 레이아웃을 필요로 하기 때문이다. 대신, 우리는 paint 루틴 동안 동적으로 계산한다. 

    여기서 우리는 우리가 외부에서 폴더들의 벡터나 배열을 생성할 수 있고 각 폴더를 그리기 위해 배열을 통하여 간단히 읽어 들일 수 있다는 것을 주목해야 한다. 그러한 배열은 다른 인스턴스들의 시리즈 처럼 낭비가 아니다. 왜냐하면 그것은 실제로는 두 개의 폴더 인스턴스 중의 하나를 참조하는 배열이기 때문이다. 그러나, ㅇ뤼는 하나의 폴더가 선택된것으로 나타내고자 하고, 폴더의 상태를 동적으로 변경시키고자 하기 때문에, 우리는 바로 각 시간에 정확하게 인스턴스를 줄 수 있는 FolderFactory를 사용한다. 
	
public void paint(Graphics g) {
	Folder f;
	String name;
	
	int j = 0;      //count number in row
	int row = Top;  //start in upper left
	int x = Left;
	
	//go through all the names and folders
	for (int i = 0; i< names.size(); i++) {
		name = (String)names.elementAt(i);
		if(name.equals(selectedName))
			f = fact.getFolder(true);
		else
			f = fact.getFolder(false);
			
		//have that folder draw itself at this spot
		f.Draw(g, x, row, name);
		
		x = x + HSpace;          //change to next posn
		j++;
		
		if (j >= HCount) {        //reset for next row		
			j = 0;         
			row += VSpace;
			x = Left;
		}
	}
}

Selecting A Folder

    우리가  선택된 상태거나 선택되지 않은 상태와 관련된 두개의 폴더 인스턴스를 갖기 때문에, 우리는 그 것들 위로 마우스를 움직여서 폴더가 선택되었는지를 알 수 있기를 원한다. 위의 paint 루틴에서 우리는 간단히 선택된 폴더의 이름을 기억하고 factory가 선택된 폴더로 반환하기를 요청한다. 폴더들이 개별적인 인스턴스들을 갖지 않기 때문에 우리는 각 폴더 인스턴스 내에서 마우스의 움직임을 감지할 수 없다. 사실, 하나의 폴더내에서 감지 할 수 있다 하더라도, 우리는 선택되지 않는 폴더의 인스턴스들에게 알려줄 방법을 가지고 있어야만 한다. 

    대신, 우리는 윈도우 단계에서 마우스의 움직임을 체크하고 만ㅇ약 마우스가 사각형 내에서 발견된다면 우리는 선택된 이름과 대응되는 이름을 만들어 낼 수 있다. 이것이 우리가 다시 그리고 필요한 곳에서 선택된 폴더 인스턴스를 생성할 때 체크할 수 있도록 허용한다.  

public void mouseMoved(MouseEvent e) {

	int j = 0;      //count number in row
	int row = Top;  //start in upper left
	int x = Left;
	
	//go through all the names and folders
	for (int i = 0; i< names.size(); i++) {
		//see if this folder contains the mouse
		Rectangle r = new Rectangle(x,row,W,H);
		if (r.contains(e.getX(), e.getY())) {
			 selectedName=(String)names.elementAt(i);
			 repaint();
		}
		x = x + HSpace;          //change to next posn
		j++;
		if (j >= HCount) {        //reset for next row
			j = 0;         
			row += VSpace;
			x = Left;
		}
	}

}
10개의 이름이 있는 폴더를 나타내는 프로그램은 아래와 같다.

    ##########0*

Flyweight Uses in Java

Flyweights는 자바에서 어프리케이션 단계에서는 자주 사용되지 않는다. 그것들은 자바보다 저수준의 단계에서 사용되는 시스템의 자원을 관리하는 기술이다. 그러나, 필요할 때 사용할 수 있기 위해서 이 기술이 존재하는 것을 알아두는 것은 유용한다. 

    Flyweights 가 사용되는 곳은 테이블과 리스트를 위해 사용되는 cell renderer 코드이다. 보통 셀 렌더러는 단지 JLabel이지만 labels의 두 세가지 타입이 있을 수 있거나 다른 색이나 폰트에 대해 렌더링을 할 수 있다. 그러나 테이블이나 리스트에서 셀보다는 렌더러가 더 적다. 

    자바언어에 있는 몇몇 객체들은 Flyweight로서 포장되어 구현될 수 있다. 예를 들어 동일한 문자들을 갖는 문자열의 인스턴스가 두개가 있다면 같은 저장 위치를 참조하게 할 수 있다. 마찬가지로 같은 값을 갖는 Interger 나 Float 객체는 Flyweight로 구현될 수 있다. Flyweights 의 부재를 증명하기 위해 다음 코드를 실행시켜 보자. 

Integer five = new Integer(5);          
Integer myfive = new Integer(5);        
System.out.println(five==myfive);
                                        
//String fred = new String("fred");     
//String fred1 = new String("fred");    
                                        
String fred ="fred";                    
String fred1 ="fred";                   
                                        
System.out.println(fred==fred1);        
    두 가지 모두 "false" 를 출력할 것이다. 그러나 "==" 연산자를 이용하여 Flyweight의 두 개의 같은 인스턴스들의 문제를 처리할 때 쉽게 결정할 수 있다는 것은 유용하다. 그것은 사실 같은가에 대한 비교가 아니라 레퍼런스(메모리 주소)를 비교하는 것이다. 

'Development > 패턴자료' 카테고리의 다른 글

[펌] The Interpreter Pattern  (0) 2011.08.13
[펌] The History of Design Patterns  (0) 2011.08.13
[펌] The Factory Pattern  (0) 2011.08.13
[펌] The Facade Pattern  (0) 2011.08.13
[펌] The Decorator Pattern  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,