2005년 2월 28일 월요일

Behavioral Patterns

Behavioral Patterns

    Behavioral 패턴들은 객체들 사이의 통신과 관련된 가장 명확한 패턴이다. 이 단원에서는 다음과 같은 패턴들을 살펴볼 것이다.

The Observer Pattern (in the Learning the Java Language trail)

Observer 패턴은 클래스들이 서로 변경을 알아 챌 수 있도록 하는 방법을 정의한다.

The Mediator Pattern (in the Learning the Java Language trail)

Mediator 패턴은 클래스간에 서로 아는 것을 갖는 것으로부터 모든 클래스들을 유지하기 위하여  또 다른 클래스를 사용함으로써 간단하게 클래스 간의 통신을 어떻게 할 지를 정의한다.

The Chain of Responsibilty Pattern (in the Learning the Java Language trail)

Chain of Responsibility 는 클래스가 인식될 때까지 클래스들 사이의 요청을 전달하여 클래스들 사이의 디커플링을 훨씬 더 허용하는 것이다. 

The Template Pattern (in the Learning the Java Language trail)

Template 는 알고리즘의 추상적인 정의를 제공한다.

The Interpreter Pattern (in the Learning the Java Language trail)

Interpreters 는 프로그램에서 언어 요소들을 어떻게 포함할 것인가에 대한 정의를 제공한다. 

The Strategy Pattern (in the Learning the Java Language trail)

Strategy 는 하나의 클래스 내부에서 알고리즘을 캡슐화한다.  

The Visitor Pattern (in the Learning the Java Language trail)

Visitor 패턴은 클래스에 함수를 추가한다.

The State Pattern (in the Learning the Java Language trail)

State 패턴은 클래스의 인스턴스 변수들을 위한 메모리를 제공한다.

The Command Pattern (in the Learning the Java Language trail)

Command 패턴은 명령의 실행을 일으키는 인터페이스 환경으로부터 명령의 실행을 분리하기 위한 간단한 방법을 제공한다.

The Iterator Pattern (in the Learning the Java Language trail)

Iterator 패턴은 클래스에 있는 데이터의 목록을 통하여 우리는 이동하는 방법을 공식화한다.

Summary of Structural Patterns

Summary of Structural Patterns

  • Adapter 패턴은 어떤 클래스의 인터페이스를 다른 클래스의 인터페이스로 변경할 때 사용한다.
  • Bridge 패턴은 클라이언트 프로그램에서 나타내거나 사용하는 실제적인 종류의 클래스들을 변경을 허용하는 동안  일정한 인터페이스를  유지하려는 의도를 가지고 있다.
  • Composite 패턴은 객체들의 수집이나 그 자체가 조합되어 있는 객체들의 수집이다. 
  • Decorator 패턴은 클래스가 주어졌을 때 그것을 새로운 가능성을 추가하고, 기저 클래스에 변경되지 않은 메소드를 건내준다. 
  • Facade 패턴은 복잡한 상속구조의 객체들의 그룹에서 그러한 데이터를 접근할 수 있는 간단한 인터페이스를 제공한다.
  • Flyweight 패턴은 작고, 유사한 클래스 인스턴스의 급증을 외부의 클래스 데이터로 옮기고 메소들일 실행되는 동안 그 데이터들을 건내주어 제한하는 방법을 제공한다. 
  • Proxy 패턴은 인스턴스를 만드는데 비용이 많이 드는 좀더 복잡한 클래스에 대하여 간단한 place-holder 클래스를 제공한다. 

The Decorator Pattern

The Decorator Pattern

    데코레이터 패턴은 새로 상속 받아 클래스를 생성하는 것 없이 개별의 객체들의 행위를 수정할 수 있는 방법을 제공한다. 8개의 객체들을 가지는 프로그램이 있다고 해보자. 그러나 그 중 3개는 추가적인 속성이 필요하다. 이러한 객체들의 각각에 대하여 상속받아 클래스를 생성할 수도 있고, 많은 경우에 이것은 완벽하게 받아 들일 수 있는 해결책일 수 있다. 그러나, 이 세 객체의 각각이 다른 수정을 요구한다면, 이것은 세 개의 상속 받은 클래스를 생성하는 것을 의미한다. 게다가, 클래스들 중에 하나가 다른 두개 클래스의 특성을 가지고 있다면, 혼란스럽고 불필요한 클래스를 생성하게 된다. 

    예를 들어, 툴바에 있는 몇몇 버튼들 주위에 특별한 테두리를 그리려 한다고 가정해 보자. 만약 우리가 파생 받은  새로운 버튼 클래스를 생성했다면, 새로운 클래스의 모든 버튼들은 의도하지 않을 때도 항상 같은 새로운 테두리를 갖음을 의미한다. 

    대신, 버튼들을 장식하는 Decorator 클래스를 생성해 보자. 그리고 나서 Decorator 클래스로부터 특별한 Decorator들을 파생시킨다.  버튼을 장식하기 위해, Decorator는 시각적인 환경으로부터 파생된 그래픽 객체이고, paint 메소드를 받는다. 그러나 Decorator는 장식할 수 있는 객체를 포함하는 것이다. 그것은 몇몇의 그래픽 메소드가 호출되는 것을 방해할 수 있다. 

Decorating a CoolButton

최근의 Internet Explorer 나 Netscape Navigator 와 같은 윈도우즈 어프리케이션들은 마우스가 올라 갔을 때는 테두리가 나타나고 그렇지 않은 때는 테두리가 없는 버튼 들의 행을 가지고 있다. 몇몇 윈도우즈 프로그래머들은 그것을 CoolBar 또는 CoolButtons라 부른다. JFC에는 그와 비슷한 작동을 하는 버튼이 없지만, 우리는 decorating JButton으로 그러한 작동을 얻을 수 있다. 이러한 경우에, 우리는 버튼 테두리 위에 회색 선을 그리거나 지워서 장식을 한다.  
    어떻게 Decorator를 생성할 지를 고려해 보자. 디자인 패턴에서는 일반적인 시각적 컴포넌트 클래스로부터 파생받아 실제적인 버튼을 위한 모든 메시지가 decorator로부터 전달될 수 있게 한다. 자바에서는 우리가 재구현해야 하는 기본적인 JComponent에서 수백개의 메소드를 호출할 수 있으므로  완벽하게 실용적이다. 

    디자인 패턴은 Decorator 와 같은 클래스들은 추상클래스이어야 하고 실질적인 작업은 파생받은 클래스에서 하도록 제안한다. 자바에서 구현은 꼭 그렇게 할 필요가 없는데, 왜냐면 상위의 Decorator 클래스는 public 메소드를 가지고 있지 않기 때문이고 public 메소드를 가지지 않은 것은 JComponent 클래스의 메소드 자체이기 때문이다. 
public class Decorator extends JComponent {   public Decorator(JComponent c) {      setLayout(new BorderLayout());      add("Center", c);   }}
    이제 어떻게 CoolButton을 구한할 수 있는지 알아보자. 우리가 해야 할 필요가 있는 것은 상위 클래스로부터 일반적인 버튼을 그리는 것이고, 버튼 주변을 회색으로 그리고 지우는 것이다. 
//this class decorates a CoolButton so that//the borders are invisible when the mouse//is not over the buttonpublic class CoolDecorator extends Decorator {   boolean mouse_over;    //true when mose over button   JComponent thisComp;   public CoolDecorator(JComponent c){      super(c);      mouse_over = false;      thisComp = this;      //save this component      //catch mouse movements in inner class      c.addMouseListener(new MouseAdapter() { 	      public void mouseEntered(MouseEvent e) {	         mouse_over=true;     //set flag when mouse over	         thisComp.repaint();	      }	      public void mouseExited(MouseEvent e) {	         mouse_over=false;    //clear flag when mouse not over	         thisComp.repaint();	      }      });   }   //paint the button   public void paint(Graphics g) {      super.paint(g);      //first draw the parent button      if(! mouse_over) {         //if the mouse is not over the button         //erase the borders         Dimension size = super.getSize();         g.setColor(Color.lightGray);         g.drawRect(0, 0, size.width-1, size.height-1);         g.drawLine(size.width-2, 0, size.width-2, size.height-1);         g.drawLine(0, size.height-2, size.width-2, size.height-2);      }   }}

Using a Decorator

지금까지는 CoolDecorator 클래스를 작성해보았는데, 그것을 어떻게 사용할 것인가? 우리는 CoolDecorator의 인스턴스를 생석할 수 있고 장식하고자 하는 버튼으로 넘겨줄 수 있다.  CoolButton과 보통의 JButton을 가지는 프로그램을 생각해 보자. 우리는 아래처럼 레이아웃을 잡을 수 있다. 
super ("Deco Button");                                         JPanel jp = new JPanel();                                                                                                     getContentPane().add(jp);                                      jp.add(new JButton("Cbutton"));                                jp.add( new CoolDecorator(new JButton("Dbutton")));            jp.add(Quit = new JButton("Quit"));                            Quit.addActionListener(this);                                  
    프로그램은 아래와 같다. 마우스가 올라 가면 버튼의 테두리가 그려질 것이다.

               

Inheritance Order

    어떤 사람들은 JComponent 로부터 상속받은 decorator를 가지고  하나의 버튼을 장식하기 때문에  혼란스럽운 Decorator들의 상속순서를 찾는다. 아래의 그림은 상속 관계를 나타낸 것이다. 


Consequences of the Decorator Pattern

    Decorator 패턴은 상속을 사용하기보다는 하나의 클래스에 책임을 추가하여 보다 유연한 방법을 제공한다. 또한 복잡한 상속관계 없이 하나의 클래스를 정의할 수 있게 해준다. 디자인 패턴은 두 가지 단점을 지적하였는데, 하나는 Decorator와 그 안에 있는 컴포넌트가 동일하지 않다는 것이다. 그래서 객체 타입에 대한 검사를 할 수 없다. Decorator 패턴의 두 번째 결점은 프로그래머가 유지해야 할 코드들을 가지는 작지만 많은 객체들을 유도할 수 있다는 것이다. 이것은 골치거리가 될 수 있다. 

The Bridge Pattern

The Bridge Pattern

    브리지 패턴은 클래스의 구현으로부터 인터페이스를 분리하는데 사용되어진다. 브리지 패턴은 추상화 정도에 의한 계층으로 구분되어 있고 또 이를 구현하는데 있어 몇 개의 계층으로 나누는 경우에 유용한 패턴이다. 추상화 정도와 구현에 따라 완전히 구별되는 여러 클래스로 나누기 보다는 이를 동적으로 조합되는 몇 개의 상이한 클래스로 관리한다. 

    어떤 생산물의 목록을 하나의 화면에 나타내고자 한다 가정해 보자. 나타내기 위한 가장 간단한 방법은 "JList" 클래스를 사용하는 것이다. 그러나, 생산물의 일부분은 팔리게 되었다면, 우리는 판매와 관련하여 하나의 표안에 생산물을 나타내고자 할 지도 모른다. 

    우리는 앞에서 어댑터 패턴에 대하여 공부했는데, 어댑터에 기반의 클래스를 생각하게 될 지도 모른다. 간단한 예제프로그램은 잘 작동을 하겠지만, 접근에 몇몇 제한사항이 있다는 것을 알게 될 것이다.

    우리의 생산물의 데이터로부터 두 가지의 디스플레이가 필요하다고 해보자. 우리가 언급했던 생산물의 목록은 단지 customer view에서 나타나고, executive view에서는 선적된 양을 보여 줄 것이다.  생산품 리스트는 보통 JList 를 이용하여 나타내고, executive view는 JTable로 나타낼 것이다. 

           

    상위의 프로그래밍 단계에서는 단지 JList 와 JTable에서 파생된 테이블과 리스트의 인스턴스만을 생성하였다.
      pleft.add("North", new JLabel("Customer view"));      pleft.add("Center", new productList(prod));            //add in execute view as table      pright.add("North", new JLabel("Executive view"));      pright.add("Center", new productTable(prod));
또, 작성된 JawList 클래스로부터 직접 productList 클래스를 파생시켰다.
public class productList extends JawtList {  public productList(Vector products) {         super(products.size());    //for compatibility      for (int i = 0; i < products.size(); i++) {         //take each strig apart and keep only         //the product names, discarding the quntities         String s = (String)products.elementAt(i);         int index = s.indexOf("--");  //separate qty from name         if(index > 0)            add(s.substring(0, index));         else            add(s);      }  }}

Building a Bridge

이제 데이터를 나타내는 리스트 방법에서 몇 가지 변경사항이 필요하다 가정해보자. 예를 들어, 생산물의 목록을 알파벳 순으로 정렬하고자 할 수도 있다. 이렇게 하기 위해서는 상속된 클래스들의 수정을 필요하게 된다. 우리가 나타내야 할 것이 두개 이상이 된다면 더더욱 끔찍한 작업이 될 것이다. 변경된 내용을 나타내기 위해 새로운 클래스를 파생시키는 것보다는 brige를 만들어 작업하는 것이 더 나을 것이다. 



우리가 가시적인 컴포넌트를 반환하고자 한다면, 스크롤이 있는 패널의 일종으로 만들 수 있다.
public class listBridge extends JScrollPane
브리지 클래스를 설계할 때, 여러 클래스의 인스턴스를 브리지가 어떻게 결정할 것인지를 결정해야 한다. 나타날 데이터의 값이 성질에 기인하여 결정할 수도 있거나, 간단한 상수에 의해서 결정될 수도 있다. 여기서는 브리지 클래스 내에 두개의 상수를 정의하는 방식을 사용하였다. 
static public final int TABLE = 1, LIST = 2;
우리는 메인 클래스의 대부분은 유지하였으며, 새로운 listBridge의 생성자만을 약간 수정하였다.
pleft.add("North", new JLabel("Customer view"));pleft.add("Center", new listBridge(prod, listBridge.LIST));//add in execute view as tablepright.add("North", new JLabel("Execute view"));pright.add("Center", new listBridge(prod, listBridge.TABLE));
listBridge 클래스를 위한 생성자는,
public listBridge(Vector v, int table_type) {	Vector sort = sortVector(v);	//sort the Vector		if(table_type == LIST)		getViewport().add(makeList(sort));	//make list		if(table_type == TABLE)		getViewport().add(makeTable(sort));	//make table}
브리지 클래스에서 중요한 차이점은 JTable 과 JList클래스를 수정없이 직접 사용할 수 있다는 것이고 그렇게 때문에 리스트나 테이블을 위한 데이터를 생성하는 데이터 모델에서 어떤 인터페이스 컴퓨팅을 시도할 수 있다는 것이다.
private JList makeList(Vector v) {	return new JList(new BridgeListData(v));}private JTable makeTable(Vector v) {	return new JTable(new prodModel(v));}
정렬된 결과는 다음 그림과 같다.



Consequences of the Bridge Pattern

  1. 브리지 패턴은 나타내거나 사용하는 클래스의 실제적인 종류의 변경을 허용하는 반면에 클라이언트 프로그램 상수에 인터페이스를 유지하는 것을 의도한다. 이것은 복잡한 사용자 인터페이스의 모듈들을 다시 컴파일하는 것을 방지하고 브리지와 실제로 보여지는 말단의 클래스만 재컴파일하는 것을 요구한다.
  2. 구현되는 클래스와 브리지 클래스를 분할하여 확장할 수 있고, 상호간의 많은 상호작용을 제거해 준다.
  3. 훨씬 쉽게 크라이언트 프로그램에서 상세한 구현을 숨길 수 있다.

The Flyweight Pattern

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개의 이름이 있는 폴더를 나타내는 프로그램은 아래와 같다.

   

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의 두 개의 같은 인스턴스들의 문제를 처리할 때 쉽게 결정할 수 있다는 것은 유용하다. 그것은 사실 같은가에 대한 비교가 아니라 레퍼런스(메모리 주소)를 비교하는 것이다. 

The Facade Pattern

The Facade Pattern

    종종 프로그램이 전개되고 개발됨에 따라, 프로그램들은 복잡해진다. 사실, 디자인 패턴을 이용하는 거에 대한 흥분은 이러한 패턴들이 때때로 프로그램의 흐름을 이해하는 것을 어렵게 하는 많은 클래스들을 생성한다는 것이다. 게다가, 그것들은 복잡한 시스템일 수도 있고, 복잡한 인터페이스를 가지고 있을 수도 있다. 

    Facade 패턴은 이러한 하위 시스템에 대한 간단한 인터페이스를 제공하여 복잡한 것을 간단히 만드는 것이다. 이 간단성이 놓여있는 클래스의 유연성을 줄이는 경우가 있지만, 보통 대부분의 약아빠진 사용자들을 제외하고는 모두에게 필요한 함수를 제공한다. 

    다행스럽게도, 우리는 Facade가 유용하게 사용될 수 있는 예에 대하여 제공되는 복잡한 시스템을 작성할 필요가 없다. 자바는 JDBC라 불리는 인터페이스를 사용하여 데이터베이스에 연결되는 클래스들의 집합을 제공한다. 프로그램 개발자가 제공하는 JDBC 연결 클래스를 이용하여 어떤 데이터베이스 든  연결할 수 있다. 몇몇 데이터베이스드은 JDBC를 이용하여 직접적인 연결을 할 수 있고 몇몇은 JDBC-ODBC bridge 클래스를 이용하여 ODBC 드라이버에 연결을 허용한다. 

    이러한 java.sql 패키지에 있는 데이터베이스 관련 클래스들은 아래의 그림처럼 휘감는 방식으로 상호작용을 하는 저수준의 클래스들의 집합의 예를 제공한다. 

       

    데이터베이스를 연결하기 위해서, Connection 클래스의 인스턴스를 사용한다. 그리고 나서, 데이터베이스 테이블들의 이름과 필드들의 이름을 찾기 위해, Connection 으로부터 DatabaseMeta 클래스의 인스턴스를 얻는 것이 필요하다. 다음, 질의를 하기 위해 SQL 질의어를 만들고 Statement 클래스를 생성하는 Connecion 을 이용한다. 문장을 실행함으로서, ResultSet 클래스르 얻을 수 있고, ResultSet 클래스에서 필드들의 이름을 찾기 위해 ResultsetMetadata의 클래스의 인스턴스를 얻는 것이 필요하다. 
그러므로, 이러한 클래스 모두를 조작하는 것은 꾀나 어려울 수 있다. 왜냐면 대부분 그런 클래스들의 호출되는 메소드들은 예외를 발생시킬 수 있고, 코딩이 복잡해 질 수 있기 때문이다. 

           

        그러나, 데이터베이스 클래스와 resultSet 클래스의 구성을 Facade로 디자인함으로써, 우리는 훨씬 더 유용한 시스템을 만들 수 있다.     

Building the Facade Classes

    어떻게 데이터베이스를 연결할 것인지 고려해보자. 우리는 우선 데이터베이스 드라이버를 읽어들여야 한다.   
try {	Class.forName(driver);} //load the Bridge drivercatch(Exception e) {	System.out.println(e.getMessage());}
    그리고 나서 데이터베이스를 연결하기 위한  Connection 클래스를 이용한다. 우리는 또한 데이터베이스에 대하여 좀더 많은 것을 알아 내기 위해 데이터베이스의 메타데이터를 얻을 수 있다: 
try {	con = DriverManager.getConnection(url);	dma = con.getMetaData(); //get the meta data}catch(Exception e) {	System.out.println(e.getMessage());}
만약 우리가 데이터베이스에서 테이블들의 이름 목록을 얻고자 한다면, 우리는 데이터베이스 메타 데이터 클래스상의 ResultSet 객체로 반환되는 getTables 메소드를 호출할 필요가 있다. 마지막으로, 이름들의 목록을 얻기 위해 그 객체들을 통하여 이터레이터를 해야 한다. 
Vector tname = new Vector();try {	results = new ResultSet(dma.getTables(catalog, null, "%", type));}catch(Exception e) {	System.out.println(e.getMessage());}while(results.hasMoreElements())	tname.addElement(results.getColumnValue("TABLE_NAME"));
이것은 관리하기가 꾀나 관리하기가 복잡해졌고, 아직은 어떤 질의어도 사용하지 않았다. 

    우리가 만들 수 있는 간략화 가정은 이러한 데이터베이스 클래스 메소드들이 발생시키는 예외를 복잡하게 핸들링 할 필요가 없게 하는 하는 것이다. 대부분은 메소드들이 데이터베이 연결에서 실패를 하지 않는다면 오류 없이 작동할 것이다. 그러므로, 우리는 오류들을 드문 드문 일어나는 오류들을 감지하고 더 이상의 행동을 취하지 않도록 클래스들의 메소드를 포장할 수 있을 것이다. 

    이것이 Connection, ResultSet, Statement 와 Metadata 클래스들의 의미있는 메소드들을 포함하는 두개의 클래스로 작성할 수 있을 것이다. 이 두 클래스는 Database 클래스이고 :
class Database {	public Database(String driver)() //constructor	public void Open(String url, String cat);	public String[] getTableNames();	public String[] getColumnNames(String table);	public String getColoumnValue(String table, String columnName);	public String getNextValue(String columnName);	public resultSet Execute(String sql);}
    이고 resultSet 클래스는 :
class resultSet {	public resultSet(ResultSet rset)	//	public String[] getMetaData();	public boolean hasMoreElements();	public String[] nextElement();	public String getColumnValue(String columnName);	public String getColumnValue(int i);}
    이 간단한 클래스들은 데이터베이스를 열고, 데이터베이스에 있는 테이블의 이름이나, 열의 이름과 내용, 간단한 쿼리에 의해  표시될 수 있도록 해준다. 

       

    그리고 텍스트 입력창에 SQL 쿼리를 입력하고 Run Query 버튼을 누르면 그에 따른 결과를 보여준다.

       

Consequences of the Facade

    Facade 패턴은 복잡한 하부 시스템의 컴포넌트로부터 클라이언트들을 포장하고 일반적인 사용자를 위한 보다 간단한 프로그래밍 인터페이스를 제공한다. 그러나 그것은 숙달된 사용자가 좀더 심도있고, 필요에 따라 클래스를 좀더 복잡하게 하는 것을 막을 수 없다. 

    게다가 Facade 는 클라이언트 코드에서 수정의 요구 없이 근원이 되는 하부 시스템에서의 변경을 할 수 있게 해주고, 복잡한 의존들을 줄여준다. 

The Proxy Pattern

The Proxy Pattern

    Proxy 패턴은 간단한 객체로 복잡한 객체를 표현하고자 할 때 사용되어진다. 만약 어떤 객체를 생성하는 것이 시간적으로나 컴퓨터 자원적으로 비용이 많이 든다면 , Proxy는 실제적인 객체를 필요로 할 때까지 객체 생성을 연기하도록 할 것이다.  일반적으로 Proxy는 표현하고자 하는 객체와 같은 메소드를 갖고, 일단 객체가 로드되면, 그것은 Proxy에서 실제적인 객체의 메소드로 바뀌게 된다. 

    Proxy 가 유용하게 사용되어 질 수 있는 유용한 경우는 다음과 같다 :      
  1. 커다란 이미지와 같은 객체가 로드하는데 시간이 많이 걸린다면 Proxy가 유용한다.
  2. 원격 머신에 객체가 있고 그 객체를 네트워크를 통하여 로드하는데 느릴 수 있고, 특히나 로드하는 동안 네트워크 상태가 절정이라면 Proxy가 유용하다.
  3. 객체가 바로 접근하는 것이 제한되어 있다면 Proxy는 사용자의 접근허용을 검증할 수 있다.
    Proxy 들은 또한 객체의 인스턴스 요청과 실제적으로 접근하는 것이 필요한가 사이를 구별하는데 사용되어 질 수 있다. 예를 들어 프로그램 초기화는 바로 사용되어 지지 않는 객체들을 설치할 수 있다. 이 경우에, proxy는 필요할 때만 실제적인 객체를 로드할 수 있다. 

    커다란 이미지를 로드하고 나타내어야 할 필요가 있는 프로그램의 경우를 고려해 보자.  프로그램이 시작될 때 이미지가 스크린에 정확하게 배치될 수 있도록 나타내 줄 수 있는 어떤 필요한 조치를 해야 한다.그러나 실제적인 이미지의 디스플레이는 이미지가 완벽하게 로드될 때까지 지연된다. 이것은 이미지가 이용되기 전에 이미지 주위에 텍스트를  배치하는 워드프로세서나 웹브라우저에서는 상당히 중요하다. 

    이미지 proxy는 이미지를 주목할 수 있고 배경에 이미지를 로딩할 수 있다. 반면에 간단한 사각형이나 다른 기호 같은 것은 그려줄 수 있다. proxy는 paint 요청이 있을 때 까지 이미지를 로딩을 지연하고, paint가 요청될 때만 로딩하게 할 수 있다.

Sample Code

    예제 프로그램에서, 우리는 이미지가 로딩 했을 때 JPanel 상에 이미지를 나타내는 프로그램을 작성하였다. 이미지를 직접적으로 로딩하는 것 보다, 우리는 로딩을 연기하고 이미지가 완벽하게 로딩될 때 까지 이미지 영역에 사각형을 그려주는 ImageProxy라 불리는 클래스를 사용할 것이다. 
	public class ProxyDisplay extends JxFrame {	public ProxyDisplay() {		super("Display proxied image");		JPanel p = new JPanel();		getContentPane().add(p);		p.setLayout(new BorderLayout());		ImageProxy image = new ImageProxy("elliott.jpg", 321, 271);		p.add("Center", image);		p.add("North", new Label("    "));		p.add("West", new Label("  "));		setSize(370, 350);		setVisible(true);	}
    단지 하나의 이미지를 포함할 수 있는 ImageProxy의 인스턴스를 생성하였고, 실제적인 이미지로써 JPanel을 추가하였다.

    ImageProxy 클래스는 이미지 로딩을 set up 하고, 생성자 내부에서 로딩 처리를 하기 위해 MediaTracker object를 생성하였다.
	class ImageProxy extends JPanel implements Runnable {	int height, width;	MediaTracker tracker;	Image img;	JFrame frame;	Thread imageCheck;        //to monitor loading		//------------------------------------		public ImageProxy(String filename, int w, int h) {		height = h;		width = w;				tracker = new MediaTracker(this);		img = Toolkit.getDefaultToolkit().getImage(filename);		tracker.addImage(img, 0);     //watch for image loading				imageCheck = new Thread(this);		imageCheck.start();           //start 2nd thread monitor				//this begins actual image loading		try {			tracker.waitForID(0,1);		}		catch(InterruptedException e){		}}
    MediaTracher 의 weightForID 메소드는 실제적인 로딩 초기화를 한다. 이 경우에, 우리는 프로그램이 지연되는 최소로 하기 위해 최소 기다리는 시간을 1 msec로 입력하였다.

    생성자는 또한 2~3 밀리세컨드에서 로딩 상태를 체크하는 imageCheck 쓰레드를 분리하여 생성하였고, 쓰레드 작동을 시작하였다.
	public void run() {	//this thread monitors image loading	//and repaints when done	//the 1000 msec is artifically long	//to allow demo to display with delay		try {		Thread.sleep(1000);		while(! tracker.checkID(0))		Thread.sleep(1000);	}		catch(Exception e){}		repaint();}
    마지막으로, Proxy는 JPanel 컴포넌트로부터 파생되었으므로, 본질적으로 paint 메소드를 갖는다. 이 메소드에서, 우리는 이미지 로딩되지 않았다면 사각형을 그려준다. 만약 이미지가 로딩되었다면, 우리는 사각형을 지워주고 대신 이미지를 그릴 것이다.  
	public void paintComponent(Graphics g) {	super.paintComponent(g);	if (tracker.checkID(0)) {		height = img.getHeight(frame);   //get height		width = img.getWidth(frame);     //and width				g.setColor(Color.lightGray);     //erase box		g.fillRect(0,0, width, height);		g.drawImage(img, 0, 0, this);   //draw loaded image	}	else {		//draw box outlining image if not loaded yet		g.setColor(Color.black);		g.drawRect(1, 1, width-2, height-2);	}}

    프로그램의 두 가지 상태는 아래의 그림처럼 설명될 수 있다.

       

Copy-on-Write

또, proxy는 변경될 수도 있고, 변경되지 않을 수도 있는 커다란 객체들을 복사하는데에도 사용되어 질 수 있다. 마약 비싼 객체의 두 번째 인스턴스를 생성한다면, Proxy는 여전히 복사본을 만들 이유가 없다는 것을 결정할 수 있다. 간단하게 워본 객체를 사용하는 것이다. 그리고 나서, 만약 프로그램이 새로운 복사로 변경되었다면, Proxy가 원본 객체를 복사할 수 있고, 새로운 인스턴스로 변경되는 것을 만들 수 있다. 이것은 객체들이 인스턴스가 된 후 항상 변경하지 않을 때 시간과 공간을 크게 줄일 수 있게 한다.