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 키르히아이스
,