'전체 글'에 해당되는 글 678건

  1. 2011.08.13 [펌] The Observer Pattern
  2. 2011.08.13 [펌] The Memento Pattern
  3. 2011.08.13 [펌] The Mediator Pattern
  4. 2011.08.13 [펌] The Iterator Pattern

The Observer Pattern

    복잡한 윈도우 세계에서 우리는 종종 데이터를 하나 이상의 폼으로 동시에 보여주고 싶고 그 데이타에서 어떤 변화라도 반사하여 보여주고 싶을 때가 있다. 예를 들어 주식 가격의 변화를 그래프와 테이블이나 리스트로 표현할 지도 모른다. 매 시간 마다 가격은 변하고 우리의 어떤 조작 없이 동시에 변화를 두 가지 방법으로 표현하기를 기대하게 될 것이다.

    우리는 우리가 행위를 볼 수 있는 Excel과 같은 윈도우 어플리케이션들이 있기 때문에  행위의 정렬을 기대한다. 이런 방식으로 작동하는 프로그램에 대해서는 Observer 디자인 패턴이 유용하게 사용될 수 있다.

    Observer 패턴은 데이터를 포함하는 객체는 데이터를 보여주는 객체로부터 분리된다는 것을 가정하고 이 보여주는 객체들은 데이터의 변화를 관찰한다(observe). 아래 그림은 설명을 위한 간단한 그림이다.

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

    우리가 Observer 패턴을 구현할 때, 우리는 일반적으로 주어(Subject)로서 데이터를 참조하고 Observers로 각각의 디스플레이를 참조한다. 각각의 observer들은 Subject에서 public 메소드를 호출하여 데이터에 그것의 관심을 등록한다. 그 다음 각 observer는 Subject가 데이터가 변경될 때 호출하기 위한 인터페이스로 알려져 있다. 우리는 이러한 인터페이스를 아래처럼 정의할 수 있다.

abstract interface Observer {
	//notify the Observers that a change has taken place
	public void sendNotify (String s);
}

abstract interface Subject {
	//tell the Subject you are interested in changes
	public void registerInterested (Observer obs);
}
이런 추상 인터페이스들을 정의하여 얻는 이점은 각각의 클래스 객체가 이 인터페이스들을 구현하기를 원하는 그 객체들의 어떤 정렬이건 작성할 수 있고, 이 객체들을 Subject 타입으로 선언할 수 있고 Observer가 어떤 일을 하는가는 문제가 되지 않는다.

Watching Colors Change

    이 강력한 개념을 어떻게 사용할 수 있는가를 설명할 수 있는 간단한 프로그램을 작성해 보자. 우리의 프로그램은 아래의 그림처럼 Red, Green 그리고 Blue라 명명된 3개의 라디오 버튼을 포함하는 프레임을 보여준다.

        ##########1*

    이 메인 윈도우는 Subject 나 데이터 저장 객체이다. 우리는 이 윈도우를 JFC 클래스들을 이용하여 다음 코드처럼 작성하였다:

public class Watch2L extends JFrame 
	implements ActionListener, ItemListener, Subject {
	
	JButton close;
	JRadioButton red, green, blue;
	Vector observers;
	
	//------------------------
	
	public Watch2L() {
		super("Change 2 other frames");
		
		//list of ovserving frames
		observers = new Vector();
		
		//add panel to cotent pane
		JPanel p = new JPanel(true);
		p.setLayout(new BorderLayout());
		getContentPane().add("Center",p);
		
		//vertical box layout
		Box box = new Box(BoxLayout.Y_AXIS);
		p.add("Center", box);
		
		//add 3 radio buttons
		box.add(red = new JRadioButton("Red"));
		box.add(green = new JRadioButton("Green"));
		box.add(blue = new JRadioButton("Blue"));
		
		//listen for clicks on radio buttons
		blue.addItemListener(this);
		red.addItemListener(this);
		green.addItemLitener(this);
		
		//make all part of same button group
		ButtonGroup bgr = new ButtonGroup();
		bgr.add(red);
		bgr.add(green);
		bgr.add(blue);
    우리의 메인 클래스는 Subject 인터페이스를 구현한 클래스라는 걸 주목하자. 그 것은 이 클래스에서 관심있는 데이터를 등록하기 위한 public 메소드를 제공해야만 한다는 것을 의미한다. 이 메소드는 registerInterest 메소이고 단지 하나의 Vector에 Observer 객체들을 추가한다:

public void registerInterest (Observer obs) {
	//adds obserers to list in Vector
	observers.addElement(obs);
}
    이제 우리가 두 개의 observers를 생성해서 하나는 색을 나타내고 다른 하나는 현재의 색을 리스트 박스에 추가하자.

//-------create observers-----------
ColorFrame cframe = new ColorFrame(this);
ListFrame lframe = new ListFrame(this);
ColorFrame 윈도우를 생성할 때 우리의 관심있는 데이터를 메인 프로그램에 등록을 한다:

class ColorFrame extends JFrame implements Observers {
	Color color;
	String color_name = "black";
	JPanel p = new JPanel(true);
	
	//---------------------------
	public ColorFrame(Subject s) {
		super("Color");		//set frame caption
		getContentPane().add("Center",p);
		s.registerInterest(this);	//register with Subject
		setBounds(100, 100, 100, 100);
		setVisible(true);
	}
	
	//-----------------------------
	public void sendNotify(String s) {
		//Observer is nofified of change here
		color_name = s;	//save color name
		//set background to that color
		if(s.toUpperCase().equals("RED"))
			color = color.red;
		if(s.toUpperCase().equals("GREEN"))
			color = color.green;
		if(s.toUpperCase().equals("BLUE"))
			color = color.blue;					
		setBackground(color);
	}
	
	//-----------------------------
	public void paint(Graphics g) {
		g.drawString(Color_name, 20, 50);
	}
그 동안 메인 프로그램에서는 라디오 버튼 중의 하나를 누를 때마다 Observers 벡터에 있는 객체들을 통하여 간단하게 실행되어 이러한 변화들에 대한 관심을 등록하는 각각의 Observer 의 sendNotify 메소드를 호출한다:

public void itemStateChanged(ItemEvent e) {
	//responds to radio button clicks
	//ifthe button is selected
	if(e.getStateChange() == ItemEvent.SELECTED)
		notifyObservers((JRadioButton)e.getSource());
	}
	
//----------------------------
private void notifyObservers(JRadioButton rad) {
	//send text of selected button to all observsers
	String color = rad.getText();
	for (int i=0; i < observers.size(); i++) {
		((Observer)(observsers.elementAt(i))).sendNotify(color);
	}
}			
    ColorFrame observer의 경우에서, sendNotify 메소드는 배경색과 프레임 안의 패널에 텍스트를 바꾼다. 그러나 ListFrame observer 경우에는 단지 리스트 박스에 새로운 색의 이름을 추가한다. 아래 그림의 최종적인 프로그램이다 :

        ##########2*
 

The Message to the Media

Subject가 observers에게 보내는 통지(notification)의 종류는 무인인가? 이  제한적인 예에서 통지 메시지는 색 자체를 표현한 문자열이다. 라디오 버튼을 클릭할 때 그 버튼에 대한 캡션을 얻고 observers 에게 그 캡션을 보낸다. 물론 이것은 모든 observer 들이 문자로 표현된 핸들링을 할 수 있다는 것이다. 보다 실제적인 상황에서 이 방법은 항상 사용할 수 있는 것은 아니다. 특히 observer들이 다른 데이터 객체들을 관찰하는데 사용되어 질 수 있다면 이 방법은 사용되지 않을 수 있다. 여기서 우리는 두 가지 간단한 데이터 변환을 시작한다:
  1. 우리는 라디오 버튼에서 라벨을 얻어 observers에 보낸다. 그리고
     
  2. ColorFrame observer 에서 실제적인 색으로 라벨을 변환한다.

    좀더 복잡한 시스템에서 우리는 요구되는 특성을 관찰할 수 있다. 각각의 observer가 메시지를 정확한 데이터 타입으로 변환하는 것보다 이런 변환을 수행할 중간의 Adapter 클래스를 사용할 수 있다.

    observer들이 처리해야 만 할지도 모르는 또 다른 문제는 중심적인 subject 클래스의 데이터가 여러 가지 방법으로 바뀔 수 있는 경우이다. 우리는 데이터의 목록에서 문제되는 것을 지우고 값을 편집하거나 데이터가 보여줄 수 있는 범위를 바꿀 수 있다. 이런 경우에 우리는 observer들에게 다른 변경 메시지를 보내거나 하나의 메시지를 보내고 발생되는 변경의 정렬을 요청하는 observer를 가지고 있을 필요가 있다.

The MVC Architecture of an Observer

JList, JTable 과 JTree 객체는 모두 데이터 모델의 observer들로 작동한다. 사실 JComponent로부터 파생 받은 모든 비쥬얼 컴포넌트들은데이터와 시각적인 표현 사이의 작업이 이와 같은 분배를 가지고 있다. JFC에서는 데이터는 모델로 표현하고 뷰는 비쥬얼 컴포넌트로 표현하는 모델-뷰-컨트롤러(Model-View-Controller : MVC) 아키텍쳐를 사용하였다. 그리고 컨트롤러는 모델과 뷰 객체들 사이의 통신이며 분할된 클래스이거나 모델이나 뷰에서 상속 받을 수도 있다.

Consequences of the Observer Pattern

    Observer들은 Subject들에게 추상적인 커플링을 조장한다. 어떤 Subject는 Subject의 observer들에 대해 자세히 알지 못한다. 그러나 이것은 데이터를 변경시키는 것이 증가할 때 Observer들을 연속적이거나 반복적으로 업데이트를 해야 하는 단점을 가질 수 있다. 업데이트 비용이 높다면 Observer들이 너무 자주 통보 받지 않도록 변경을 관리하는 어떤 정렬을 도입할 필요가 있다.

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

[펌] The Proxy Pattern  (0) 2011.08.13
[펌] The Prototype Pattern  (0) 2011.08.13
[펌] The Memento Pattern  (0) 2011.08.13
[펌] The Mediator Pattern  (0) 2011.08.13
[펌] The Iterator Pattern  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

The Memento Pattern

    어떤 객체의 내부 상태를 저장하고 그래서 후에 다시 저장 할 수 있게 한다 가정해 보자. 관념상으로는 저장할 수 있고 객체를 다시 생성하지 않고 재 저장할 수 있다. 이것이 Memento 패턴의 목적이다.

Motivation

객체들은 종종 public 메소드를 사용하여 그것들의 내부 상태의 단지 몇 개만 노출하지만, 나중에라도 그것을 다시 저장 해야 할 필요가 있을지도 모르기 때문에 어떤 객체의 전체적인 상태를 저장하고자 한다. 몇몇 경우에서 데이터를 저장하고 재 저장하는 public 인터페이스(그래픽 객체들의 위치를 그리는 것과 같은 것)로부터 충분한 정보를 얻을 수 있다. 다른 경우에는 색, 그림자, 각 과 다른 그래픽 객체들과의 관계 같은 정보는 쉽게 이용할 수 없다. 저장과 재저장의 정보에 대한 정렬은 취소 명령을 지원하는 시스템에서는 공통적인 것이다.

    하나의 객체를 설명하는 모든 정보가 public 변수로 이용할 수 있다면 외부로  저장하는 것은 어렵지가 않다. 그러나 이러한 데이터들을 공개하는 것은 외부 프로그램 코드에 의하여 쉽게 변경되는 시스템을 만들게 된다.

    Memento 패턴은 저장하고자 하는 객체의 상태를 권한 있는 접근을 갖게 하여 이 문제를 풀려고 시도한다. 다른 객체들은 그 객체에 더 제한된 접근을 가져 캡슐화를 유지한다. 이 패턴은 객체들에 대해 세가지 규칙을 정의한다:
  1. Originator는 우리가 저장하고자 하는 상태를 갖는 객체이다.
     
  2. Memento는 Originator의 상태를 저장하는 또 다른 객체이다.
     
  3. Caretaker는 상태를 저장하는 타이밍을 관리하고 Memento를 저장한다. 필요하다면 Originator의 상태를 재저장하는 Memento를 이용한다.

Implementation

어떤 객체의 모든 변수들을 공개적으로 이용할 수 있도록 만들지 않고 객체의 상태를 저장하는 것은 트릭이다. 자바에서는 이 권한 있는 접근은 약간만 알려줘 사용할 수 있고 드물게 접근 제한 방법을 사용한다. 자바 클래스에서 변수는 다음처럼 선언될 수 있다.
  1. Private
  2. Protected
  3. Public
  4. (Private protected)

    선언이 없는 변수들은 private protected로 취급된다. 다른 클래스들은 public  변수들로 접근할 수 있고, 상속된 클래스들은 protected 변수로 접근할 수 있다. 그러나 같은 모듈에서 또 다른 클래스들은 protected 나 private-protected 변수들로 접근될 수 있다. 이것은 Moment 객체들을 만들기 위해 사용할 수 있는 자바의 마지막 속성이다.  예를 들어 같은 모듈에서 선언된 클래스 A 와 B가 있다고 하자 :


public class A {
	int x, y;
	public Square() {}
	x=5;	//initialize x
}

//-----------------------------------

class B {
	public B() {
		A a = new A();	//create instance of A
		System.out.println(a.x)	//has access to variables in A
	}
}
클래스 A는 private-protected 변수 x를 포함하고 있다. 같은 모듈에 있는 클래스 B에서 자동으로 x에 5를 초기화하는 A의 인스턴스를 생성했다. 클래스 B는 클래스 A에 있는 변수 x에 직접적인 접근을 할  수 있고 컴파일이나 런타임 에러 없이 프린트 할 수 있다. 이것이 Momento 를 생성하는데 사용할 정확한 특징이다.

Sample Code

사각형을 생성하는 드로잉 프로그램의 프로토타입을 고려해 보자. 그리고 마우스를 이용하여 사각형들을 선택하고 움직일 수 있도록 해보자. 이 프로그램은 세 개의  버튼을 포함한 투라를 가지고 있다: Rectangle, Undo, Clear:

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

Rectangle 버튼은 JToggleButton이다. 일단 사각형을 그리면 어떤 사각형이건 선택할 수 있다;

        ##########1*

그리고 일단 선택되면, 사각형을 마우스를 이용하여 새로운 위치로 드래그할 수 있다.

Undo 버튼은 연산자들의 순서를 취소할 수 있다. 특히 그것은 사각형 이동을 취소하거나 생성을 취소할 수 있다.

    우리가 이 프로그램에서 반응할 필요가 있는 다섯 가지 액션이 있다.
  1. Rectangle 버튼이 클릭
  2. Unto 버튼 클릭
  3. Clear 버튼 클릭
  4. 마우스 클릭
  5. 마우스 드래그

    세 개의 버튼은 Command 객체로 생성되질 수 있고 마우스 클릭과 드래그는 역시 Command 로 다룰 수 있다. 이것은 Mediator 패턴을 사용하는 기회를 제공하고 사실 이 프로그램은 이와 같은 방법으로 작성된다.

    더구나 Mediator는  취소 액션 목록을 관리하는데 적격이다. 그러므로 Mediator는 위에서 설명했던 Caretaker 객체의 기능을 한다. 사실 그러한 프로그램에서 저장하고 취소하는 액션이 얼마든지 가능하기 때문에 Mediator는 이런 명령들이 나중에 취소하기 위해 저장될 수 있는 가상적인 하나의 장소이다.

    이 프로그램에서 우리는 저장과 취소 단지 두 가지 동작: 새로운 사각형을 생성하고 사각형의 위치를 변경만이 있다.  사각형의 인스턴스를 실제적으로 그리는 visRectangle 클래스 먼저 시작해 보자:


public class visRectangle {
	int x, y, w, h;
	Rectangle rect;
	boolean selected;
	
	public visRectangle(int xpt, int ypt) {
		x = xpt;   y = ypt;
		w = 40;    h = 30;
		saveAsRect();
	}
	
	//-------------------------------------------
	public void setSelected(boolean b) {
		selected = b;
	}
	
	//-------------------------------------------
	private void saveAsRect() {
		rect = new Rectangle(x-w/2, y-h/2, w, h);
	}
	
	//-------------------------------------------
	public void draw(Graphics g) {
		g.drawRect(x, y, w, h);
		
		if (selected) { //draw "handles"
			g.fillRect(x+w/2, y-2, 4, 4);
			g.fillRect(x-2, y+h/2, 4, 4);
			g.fillRect(x+w/2, y+h-2, 4, 4);
			g.fillRect(x+w-2, y+h/2, 4, 4);
		}
	}
	
	//-------------------------------------------
	public boolean contains(int x, int y) {
		return rect.contains(x, y);
	}
	
	//-------------------------------------------
	public void move(int xpt, int ypt) {
		x = xpt; y = ypt;
		saveAsRect();
	}
}
    사각형을 그리는 것은 꾀나 수월하다. 이제, 간단한 같은 visRectangle.java 파일에 있어 위치와 크기변수들에 접근할 수 있는 Memento 클래스를 보자.

class Memento {
	visRectangle rect;
	//saved fields- remember internal fields
	//of the specified visual rectangle
	int x, y, w, h;
	
	public Memento(visRectangle r) {
		rect = r;
		x = rect.x;  y = rect.y;
		w = rect.w;  h = rect.h;
	}
	
	//-------------------------------------------
	public void restore() {
		//restore the internal state of
		//the specified rectangle
		rect.x = x;  rect.y = y;
		rect.h = h;  rect.w = w;
	}
}
우리가 Memento 클래스의 인스턴스를 생성했을 때 저장하고자 하는 visRectangle 인스턴스를 Memento 클래스에 전달한다. 그것은 크기와 위치 파라미터들을 복사하고 visRectangle 그 자체의 인스턴스의 사본을 저장한다. 후에 우리가 이러한 파라미터들을 다시 저장하려고 할 때 Memento 재저장하고 직접 할 수 있는 인스턴스를 알고 있다.

    나머지 동작은 우리가 취소 목록에 Integer로 드로잉의 목록의 전 단계를 저장하는  Mediator 클래스에서 이루어 진다.

public void createRect(int x, int y) {                                         
	unpick();   //make sure no rectangle is selected                       
	                                                                       
	if(startRect) {                                                        
		//if rect button is depressed                                  
		Integer count = new Integer(drawings.size());                  
		undoList.addElement(count);   //Save previous drawing list size
		visRectangle v = new visRectangle(x, y);                       
		drawings.addElement(v);       //add new element to list        
		startRect = false;            //done with this rectangle       
		rect.setSelected(false);      //unclick button                 
		canvas.repaint();                                              
	}                                                                      
	else                                                                   
		pickRect(x, y); //if not pressed look for rect to select       
}                                                                              
    그리고 Memento에 옮기기 전에 사각형의 전 위치를 저장한다:

public void rememberPosition() {                           
	if(rectSelected) {                                  
		Memento m = new Memento(selectedRectangle);
		undoList.addElement(m);                    
	}                                                  
}                                                          
    우리의 undo 메소드는 간단히 드로잉 리스트에서 Memento에 의해 축소하거나  Memento의 restore 메소드를 호출할지를  결정한다 :

public void undo() {                                                         
	if(undoList.size()>0) {                                              
		//get last element in undo list                              
		Object obj = undoList.lastElement();                         
		undoList.removeElement(obj);   //and remove it               
		                                                             
		//if this is an Integer, the last action was a new rectangle 
		if (obj instanceof Integer) {                                
			//remove last created rectangle                      
			Object drawObj = drawings.lastElement();             
			drawings.removeElement(drawObj);                     
		}                                                            
		                                                             
		//if this is a Memento, the last action was a move           
		if(obj instanceof Memento) {                                 
			//get the Memento                                    
			Memento m = (Memento)obj;                            
			m.restore();     //and restore the old position      
		}                                                            
		                                                             
		repaint();                                                   
	}                                                                    
}                                                                            

Consequences of the Memento Pattern

    Memento는  캡슐화가 가능한 언어에서 캡슐화를 유지하면서 어떤 객체의 상태를 저장하는 방법을 제공한다. 그러므로 유일한 Originator 클래스에서의 데이터는 private를 유지하는 효과적인 접근을 한다. 그것은 또 Memento 클래스에 정보를 저장 또는 재저장을 위임함으로써 Originator 클래스의 단순성을 보호한다.

    반면에 Memento 저장해야 하는  정보의 양이 클 수도 있다. 그래서 저장하는데 시간이 많이 걸릴 수 있다. 이것은 상태를 저장하기 위한 객체들의 수를 제한하는 전략을 설계할 수 있는 Caretaker 클래스(여기서는 Mediator)에서는 보다 효율적이다. 우리의 예제에서 그러한 제한들을 부과하지 않았다. 예측할 수 있는 방법으로 객체들이 바뀌는 경우에는 각각의 Memento는 객체의 상태의 단지 추가적인 변경만을 저장함으로써 얻을 수 있을지도 모른다.

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

[펌] The Prototype Pattern  (0) 2011.08.13
[펌] The Observer Pattern  (0) 2011.08.13
[펌] The Mediator Pattern  (0) 2011.08.13
[펌] The Iterator Pattern  (0) 2011.08.13
[펌] The Interpreter Pattern  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

The Mediator Pattern

    어떤 프로그램이 여러 개의  클래스로 만들어져 있을 때 로직과 계산은 이러한 클래스로부터 논리적으로 나뉘어 진다. 그러나 이러한 고립적인 클래스들이 증가함에 따라 이러한 클래스들의 통신 문제가 보다 복잡해졌다. 다른 클래스의 메소드들에 대하여 아는 것이 필요한 클래스가 더 많아지고 클래스들의 구조는 더 얽혀 복잡해 진다. 이것이 프로그램을 읽는 것과 유지를 어렵게 한다. 게다가 프로그램을 변경하는 것이 어려운데 왜냐면 어떤 변화가 여러 개의 다른 클래스들에서 코드에 영향을 줄 지도 모르기 때문이다. Mediator 패턴은 이러한 클래스들 사이의 느슨한 커플링을 진행하여 이러한 문제를 해결할 수 있는 방법을 소개한다. Mediator는 다른 클래스들의 메소드들에 대한 정보를 설명한 오직 하나의 클래스의 이용하여 이 문제를 해결할 수 있다. 클래스들은 변경되었을 때 mediator에게 정보를 준고 Mediator는 정보를 필요로 하는 다른 클래스들에게 정보를 전달해 준다.

An Example System

    여러 개의 버튼과 두 개의 리스트 박스와 입력할 수 있는 텍스트 필드를 갖는 프로그램을 생각해 보자:


    프로그램이 시작되었을 때는 Copy 와 Clear 버튼은 비 활성 상태이다.
  1. 좌측의 리스트 박스에서 이름들 중의 하나를 선택했을 때, 편집을 위한 텍스트 필드에 복사가 되고 Copy 버튼은 활성화 된다.
     
  2. Copy 버튼을 눌렀을 때, 그 텍스트는 우측의 리스트 박스에 추가되고 Clear 버튼이 활성화 된다.

  3. Clear 버튼을 눌렀다면 우측의 리스트 박스와 텍스트 필드는 지워지고 리스트 박스는 선택할 수 없고 두 개의 버튼은 다시 비 활성 상태가 된다.
    이와 같은 사용자 인터페이스는 공통적으로 사람이나 생산물의 목록의 선택에서 사용되어진다. 게다가 이렇게 만든 것보다 삽입, 삭제  와 취소와 같은 기능이 추가되어 더 복잡해 질 수 있다.

Interactions between Controls

시각적인 컨트롤들 사이에 상호작용은 이러한 간단한 예 조차 꾀 복잡하다. 각각의 시각적인 객체는 두 개나 더 이상의 객체들에 관해서 알 필요가 있어 아래 그림처럼 관계들이 얽혀 있을 수 있다.

    Mediator 패턴은 이 시스템에서 다른 클래스들을 인식할 수 있는 오직 하나의 클래스 두어 이 시스템을 단순화한다. Mediator 와 통신 하는 각각의 컨트롤들을 Colleague 라 부른다. 각각의 Colleague 는 사용자 이벤트를 받았을 때 Mediator 에게 통지하고 Mediator 는 이벤트가 통지 될 수 있는 다른 클래스들을 결정한다. 아래 그림은 상호 작용 구조를 단순화한 것이다.

    Mediator의 장점은 명확하다 -- 그것은 다른 클래스들에 대해 아는 오직 하나의 클래스를 갖고 그래서 다른 클래스들 중의 하나가 바뀌거나 다른 인터페이스 컨트롤 클래스가 추가된다면 그 클래스는 변경할 필요가 있다.

Sample Code

이 프로그램을 자세하게 고려하고 각각의 컨트롤이 어떻게 구축할 지를 결정해 보자. Mediator 클래스를 사용하는 프로그램 작성에서 주요 차이점은 각각의 클래스들은 Mediator의 존재를 알 필요가 있다. Mediator의 인스턴스를 만들어 시작하고 각 클래스의 생성자에 Mediator의 인스턴스를 전달한다. 

Mediator med = new Mediator();
kidList = new KidList(med);
tx = new KTextField(med);
Move = new MoveButton(this, med);
Clear = new ClearButton(thism med);
med.init();
    우리는 각 컨트롤에 대해 새로운 클래스들을 만들었기 때문에 각 클래스 내에서 mediator 연산자를 조정할 수 있다.

    두 개의 버튼은 Command 패턴을 사용했고 초기화 되는 동안 Mediator에 등록하였다. CopyButton에 대한 코드는 아래와 같다 :

public class MoveButton extends JButton implements Command {
	Mediator med;            //copy of the Mediator
	
	public MoveButton(ActionListener fr, Mediator md) {
		super("Copy");         //create the button
		addActionListener(fr); //add its listener
		med = md;              //copy in the Mediator instance
		med.registerMove(this); //register with the Mediator   
	}
	
	public void Execute() {
		//execute the copy
		med.Move();
	}
}
Clear 버튼은 거의 비슷하다.
   
    아이의 이름 목록은 최근에 예에서 사용한 것에 기본을 두고 있지만 리스트의 데이터 로딩과 Mediator에 등록을 하기 위해 확장을 하였다. 추가적으로 우리는 ListSelectionListener를 구현하였고 어떤 리스트 항목을 누르건 Mediator에 정보를 줄 수 있도록 하였다.

public class KidList extends JawtList implements ListSelectionListener {
	KidData kdata;
	Mediator med;
	
	public KidList(Mediator md) {
		super(20);
		kdata = new KidData ("50free.txt");
		fillKidList();
		med = md;
		med.registerKidList(this);
		addListSelectionListener(this);
	}
	    
	//----------------------------------
	public void valueChanged(ListSelectionEvent ls) {
		JList obj = (JList)ls.getSource();
		if (obj.getSelectedIndex() >= 0)
		med.select();
	}
	
	//----------------------------------
	private void fillKidList() {
		Enumeration ekid = kdata.elements();
		while (ekid.hasMoreElements()) {
			Kid k =(Kid)ekid.nextElement();
			add(k.getFrname()+" "+k.getLname());
		}
	}
}
    텍스트 필드는 그 자체를 mediator에 등록을 하기 때문에 훨씬 간단하다.

public class KTextField extends JTextField {
	Mediator med;
	
	public KTextField(Mediator md) {
		super(10);
		med = md;
		med.registerText(this);
	}
}
    이러한 클래스들 모두의 공통점은 각각의 것들이 Mediator에 대하여 알고 Mediator에게 그것의 존재를 알려줘 Mediator는 적절한 시기에 명령들을 보내 줄 수 있다.

    Mediator 그 자체는 아주 간단하다. 그것은 Copy, Clear 와 Select 메소드를 지원하고 각 컨트롤들을 위해 메소드들을 등록해 둔다:

public class Mediator {
	private ClearButton clearButton;
	private MoveButton moveButton;
	private KTextField ktext;
	private KidList klist;
	private PickedKidsList picked;
	
	public Mediator() {
	}

	//------------------------------------
	public void Move() {
		picked.add(ktext.getText());
		clearButton.setEnabled(true);
	}
	
	//------------------------------------
	public void init() {
		Clear();
	}
	
	//------------------------------------
	public void Clear() {
		ktext.setText("");
		moveButton.setEnabled(false);
		clearButton.setEnabled(false);
		picked.clear();
		klist.clearSelection();
		System.out.println("cleared");
	}
	
	//------------------------------------
	public void select() {
		String s = (String)klist.getSelectedValue();
		ktext.setText(s);
		moveButton.setEnabled(true);
		System.out.println("selected");
	}
	
	//------------------------------------
	public void registerClear(ClearButton cb) {
		clearButton = cb;
	}
	
	//------------------------------------
	public void registerMove(MoveButton mv) {
		moveButton = mv;
	}
	
	//------------------------------------
	public void registerText(KTextField tx) {
		ktext = tx;
	}
	
	//------------------------------------
	public void registerPicked(PickedKidsList pl) {
		picked = pl;
	}
	
	//------------------------------------
	public void registerKidList(KidList kl) {
		klist = kl;
	}
}
시스템의 초기화

    Mediator를 가장 정교하게 하는 연산자 중의 하나는 요구되는 상황에 대한 모든 컨트롤의 초기화이다. 언제 우리가 프로그램을 시작할 때 각 컨트롤들은 기본 상태에서 알려져 있어야 한다. 왜냐면 이러한 상태들은 프로그램이 진화함에 따라 변경될 수 있기 때문에 우리는 간단히 요구되는 모든 상태에 대해 세팅해 주는 init 메소드를 Mediator에 만들었다. 이 경우에 상태는 Clear 버튼에 의해 이루어지는 것과 같고 우리는 간단하게 이 메소드를 호출할 수 있다 :

public void init() {
    Clear();
}

Mediators and Command Objects

이 프로그램에서 두 개의 버튼은 command 객체들이고 이러한 버튼들을 초기화 할 때 ActionListener로 사용자 인터페이스 프레임에 등록을 하였다. 우리가 앞에서 보았던 것처럼 이것은 간단하게 버튼 클릭 이벤트를 처리한다.

public void actionPerformed(ActionEvent e) {
    Command comd = (Command)e.getSource();
    comd.Execute();
}
대안적으로 우리는 자신의 리스너를 가진 상속 받은 클래스를 등록하여 Mediator에 직접 결과를 전달할 수도 있다.

    그러나 두 가지 모두 Command 패턴 단원에서 보았던 문제들에 대한 해결책 중의 하나이다.

Consequences of the Mediator Pattern

  1. Mediator는 프로그램에서 객체들 사이의 가능한 느슨한 커플링을 만든다. 또 그 밖에 것들이 여러 객체들 사이에서 방해할 수 있는 행동을 줄여줄 수 있다.
     
  2. 간단하게 바꾸거나 Mediator의 하위 클래스로 프로그램의 행위를 바꿀 수 있다.
     
  3. Mediator 접근은 새로운 Colleague를 프로그램의 다른 부분을 변경하는 것 없이 추가할 수 있다.
     
  4. Mediator는 사용자 인터페이스에서 나머지 객체들이나 메소들에 대해 너무 많이 아는 Command 객체의 문제를 해결한다.
     
  5. Mediator는 바꾸기 힘들고 유지하기 힘든 복잡한 덩어리가 될 수 있다. 종종 Mediator에 주어진 책임들을 수정하여 이 상황을 개선할 수 있다. 각 객체는 자신의 일을 수행하고 Mediator는 단지 객체들 사이의 상호작용만 관리한다.
     
  6. 각각의 Mediator는 각각의 Colleague들을 호출할 수있는 메소드를 가지고 있고  각각의 Colleague가 이용하는 메소드들이 무엇인지를 알 수 있도록 작성된 클래스이다. 이것이 다른 프로젝트에서 Mediator 코드가 재사용되는 것을 어렵게 한다. 반면에 Mediator들은 아주 간단한고 이 코드를 작성하는 것이 어떤 방법보다 객체들의 복잡한 상호작용을 관리하는 것 보다 훨씬 더 쉽다.


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

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

The Iterator Pattern

    Iterator 는 디자인 패턴에서 가장 간단하고 가장 빈번하게 사용되는 패턴들 중의 하나이다. Iterator 패턴은 데이터의 내부적인 표현을 자세하게 아는 것 없이 표준적인 인터페이스를 이용한 데이터의 리스트나 컬렉션을 통하여 이동하는 것을 허용한다. 게다가 어떤 특별한 프로세싱과 데이터 컬렉션의 특정한 원소를 반환을 하는 특별한 Iterator를 정의할 수 있다.

Motivation

    Iterator는  어떻게 Iterator 가 데이터를 이동시키는지를 드러내지 않고 데이터 원소들의 집합을 통하여 이동하는 정의된 Iterator는 인터페이스(interface)이기 때문에 반환되는 데이터를 위한 편리한 어떤 방법이든 구현할 수 있다.

public interface Iterator {
	public Object First();
	public Object Next();
	public boolean isDone();
	public Object CurrentItem();
}
여기서 리스트의 최상위로 이동할 수 있고 리스트를 통하여 이동하고, 원소들이 더 있는지를 알아내고, 현재 리스트의 항목을 찾을 수 있다. 이 인터페이스는 구현하기가 쉽고 확실한 장점들을 가지고 있지만, 자바에서 Iterator의 선택은 Enumeration 타입으로 만들어져 있다 (자바 초창기에는 그랬지만, 현재는 Iterator 가 인터페이스로 존재한다).

public interface Enumeration {
	public boolean hasMoreElements();
	public Object nextElement();
}
    리스트의 상위로 이동하는 메소드를 가지고 있지 않은 것이 첫째로 제한적인 것처럼 보여지지만 자바에서는 그것은 심각한 문제가 아니다. 왜냐면 리스트를 통하여 이동하고자 할 때마다 Enumeration의 새로운 인스턴스를 얻는 것이 통상적이기 때문이다. 자바 Enumeration의 한가지 단점은 자바 언어의 강한 형 변환이다. 이것은 hasMoreELements() 메소드가 반환된 Object 타입을 실제적인 타입으로 캐스팅하는 귀찮은 요구 없이 컬렉션에서 데이터의 실제적인 타입의 객체를 반환하는 것을 방해한다. 그러므로 Iterator 나 Enumeration 인터페이스는 다형성을 의도하는 것이다.

Enumerations in Java

Enumeration 타입은 Vector 와 Hashtable 클래스들로 만드어져 있다. 두 클래스는 클래스의 데이터의 Enumeration을 반환하는 elements 메소드를 포함하고 있다.

public Enumeration elements();

    이 elements() 메소드는 실제로 Enumeration 클래스의 인스턴스를 생성하는 Factory 메소드의 일종이다.

    그리고 나서 다음 코드를 가지고 리스트를 통해 이동시킨다.

Enumeration e = vector.elements();
while (e.hasMoreElements()) {
	String name = (String)e.nextElement();
	System.out.println(name);
}
    추가적으로 HashTable은 테이블에서 각 원소에 키들의 enumeration을 반환하는 keys 메소드를 가지고 있다.

public Enumeration keys ();

    이것이 자바에서 Enumeration을 구현하기 위한 발탁된 스타일이고 같은 데이터의 enumeration들을 동시에 동적으로 얼마든지 가질 수 있는 이점이 있다.

Filtered Iterators

하나의 컬렉션을 통하여 정확히 정의된 이동 메소드를 갖는 것은 도움이 되고, 반환되기 전의 데이터에 대한 몇 가지 계산을 수행하여 걸러진 Enumerations 을 정의할 수 있다. 예를 들어 특별한 방법으로 정렬된 데이터를 반환할 수 있거나  특정 영역에 매치되는 단지 몇 개의 객체들을 반환할 수 있다. 그래서, 걸려진 enumerations 들을 위한 매우 유사한 인터페이스들을 많이 같이 가지고 있는것 보다 같은 메소드를 갖는 이러한 enumerations 의 각각의 것을 가지고서  enumeration 의 각 타입을 반환하는 메소들 간단히 제공한다.

Sample Code

Interpreter 단원에서 설명했던 수영선수들, 클럽들, 시간들의 목록을 재사용하고 KidData 클래스에  몇 가지 기능을 추가해 보자. 

public class KidData {
	Vector kids;
	//------------------------------------------   
	public KidData(String filename) {
		kids = new Vector();
		InputFile f = new InputFile(filename);
		String s = f.readLine();
		
		while(s != null) {
			if(s.trim().length() > 0) {
				Kid k = new Kid(s);
				kids.addElement(k);
			}
		
			s = f.readLine();
		}
	}
	
	//--------------------------------
	public Enumeration elements() {
		//return an enumeration of the kids
		return kids.elements();
	}	
    컬렉션에서 모든 Kids의 enumeration을 얻기 위해, 우리는 간단히 Vector 그 자체의 enumeration을 반환한다.

The Filtered Enumeration

    그러나 우리가 어떤 클럽에 속해 있는 그러한 아이들에 대해서만 enumerate 하기를 원했었다고 가정해 보자. 이것은 KidData 클래스에서 데이터에 접근하는 특별한 Enumeration 클래스를 필요로 한다. 그런 접근을 하도록 정의된 elements() 메소드 때문에 이것은 간단하다. 그리고 나서 우리는 특정의 클럽에 속한 아이들을 반환하는 Enumeration 을 작성이 필요하다.

public class kidClub implements Enumeration {
	String clubMask;     //name of club
	Kid kid;             //next kid to return
	Enumeration ke;      //gets all kids
	KidData kdata;       //class containing kids
	
	//----------------------------------------
	
	public kidClub(KidData kd, String club) {
		clubMask = club;     //save the club
		kdata = kd;          //copy the class
		kid = null;          //default
		ke = kdata.elements();  //get Enumerator
	}
	
	//----------------------------------------
	
	public boolean hasMoreElements() {
		//return true if there are any more kids 
		//belonging to the specified club
		boolean found = false;
		while(ke.hasMoreElements() && ! found) {
			kid = (Kid)ke.nextElement();
			found = kid.getClub().equals(clubMask);
		}
		if(! found) 
		kid = null;    //set to null if none left
		return found;
	}
	//----------------------------------------
	public Object nextElement() {
		if(kid != null)
			return kid;
		else
			//throw exception if access past end
			throw new NoSuchElementException();
	}
}
    모든 작업은 생성자에서 정해진 클럽에 속한 또 다른 아이에 대한 컬렉션을 통하여 읽은 hasMoreElements() 메소드에서 마쳐지고, kid 변수에 kid를 저장하거나 더 이상 아이들이 없으면 예외를 발생시킨다. 정상적인 상황에서 이 예외는 절대로 발생되지 않는다.

    마지막으로 우리는 KidData에 새로게 걸러진 Enumeration을 반환하는 메소드 추가가 필요하다.

	public Enumeration kidsInClub(String club) {
		return new kidClub(this, club);                                       
	}
이 간단한 메소드는 클럽 이니셜과 관련된 Enumeration 클래스 kidClub에 KidClub의 인스턴스를 전달한다. 간단한 프로그램은 아래와 같고 좌측에 모든 아이들에 대하여 나타내고 우측에 속해있는 클럽들을 나타낸다.

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

Consequences of the Iterator Pattern

  1. 데이터 수정. iterators 에 대한 가장 의미 있는 질문은 데이터가 변경되는 동안 데이터를 통하여 iterating의 질문이다. 코드가 광범위해지고 오직 가끔 다음 원소로 이동한다면, 그것을 통하여 이동하는 동안에 기본적인 컬렉션에서 어떤 원소가 추가되거나 삭제될 수 있다. 또, 그것은 그 컬렉션을 변경할 수 있는 또 다른 쓰레드가 가능하다.
     
  2. 권한 있는 접근. Enumeration 클래스들은  원래의 컨테이너 클래스의 기본적인 자료 구조에 권한 있는 접근의 몇몇 정렬을 필요로 한다. 그래서 그것들은 데이터를 통해서 이동할 수 있다. 만약 데이터가 벡터나 해쉬테이블에서 정렬되어 있다면 이동하기가 더 쉽지만, 클래스에 포함된 다른 컬렉션 구조가 있다면 get 연산자를 통하여 이용할 수 있는 구조를 만들어 줘야 한다. 대신 견제하는 클래스로부터 상속 받은  Iterator 클래스를 만들 수 있고 데이터를 직접 접근할 수 있다.
     
  3. 외부 대 내부 Iterators. 디자인 패턴 교재에서는 Iterators 의 두 가지 타입을 설명하고 있다 : 외부 와 내부. 우리는 외부 Iterator 만 설명했다. 내부의 Iterator 는 전체적인 컬렉션을 통하여 이동되는 방법이고 사용자의 특별한 요청 없이 각 원소에 대해 직접적인 연산을 수행한다. 이러한 것들은 자바에서 공통적인 것은 아니지만 데이터의 값을 0 과 1사이의 값으로 컬렉션을 정규화하는 메소드를 상상할 수 있다.

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

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