2011년 5월 23일 월요일

The Memento Pattern

18. The Memento Pattern
객체에 대한 데이터를 저장하고 복원할 수 있게 하는 Memento. 패턴의 사용방법에 대해 설명할것이다. 예를 들어, 페이팅 프로그램에서 객체의 색상, 크기, 무늬 및 형태에 대한 정보를 저장할 경우를 생각해 보자. 이런 경우 이런 상태 정보를 저장하고 복원하는 작업을 각각의 객체들이 해당 작업 내용을 관여하지 않도록 하면서 켑슐화를 저해하지 않고도 수행하게 하는 것이 이상적인 결과이다. 그리고 이것이 바로 Memento 패턴의 목적이기도 하다.

구조


역할
  • Originator : 우리가 저장하고자 하는 상태를 갖는 객체이다.
  • Memento : Originator의 상태를 저장하는 또 다른 객체이다.
  • Caretaker : 상태를 저장하는 타이밍을 관리하고 Memento를 저장한다. 필요하다면 Originator의 상태를 재저장하는 Memento를 이용한다.


의도
encapsulation을 깨지 않고서 object의 내부 정보를 외부에 저장해놨다가 나중에 다시 복구 할 수 있다.

적용시기
Memento 패턴을 사용하면 프로그램에 대해 다음을 실행할 수 있습니다.
  • Undo(다시하기)
  • Redo(재실행)
  • History(작업 이력의 작성)
  • Snapshot(현재 상태의 보존)


결론
  • Memento 패턴은 캡슐화 상태를 보존하는 동안 객체의 상태를 유지하는 방법을 제공한다. 이방법은 언어적인 측면에서 가능하다. 따라서 Originator 클래스인 접근 가능한 데이터만 private 모드로 남는다. 또한 패턴이 제공하는 것은 Memento 클래스에 정보를 저장하고 복원할 책임을 부여해서 originator 클래스의 단순함을 유지하는 것이다.
  • 그러나 Memento 클래스에 저장해야 할 데이터의 양이 상당히 클 것으로 예상되기 때문에 충분한 공간을 보유해야 한다. 이것은 caretaker 클래스에도 영향을 미치게 되는데, 해당 클래스에 대해 상태 정보를 저장하는 대상 객체의수를 제한하는 설계전략을 세워놓아야 한다.


예제소스

예제 소스
import java.util.*;
public class Caretaker {
private Vector undoList;
private Vector drawings;
public Caretaker(Vector drw) {
undoList = new Vector();
drawings = drw;
}
public void rememberPosition(visRectangle rect) {
Memento m = new Memento(rect);
undoList.addElement(m);
}
public void clear(Vector drw) {
undoList = new Vector();
drawings = drw;
}
public void addElement(Object obj) {
undoList.addElement (obj);
}
private void remove(Integer obj) {
Object drawObj = drawings.lastElement();
drawings.removeElement(drawObj);
}
private void remove(Memento obj) {
Memento m = (Memento)obj;
m.restore();
}
public void undo() {
if (undoList.size() > 0) {
Object obj = undoList.lastElement();
undoList.removeElement(obj);
if (obj instanceof Integer)
remove((Integer)obj);
else
remove((Memento)obj);
}
}
}
public class Memento {
visRectangle rect;
private 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() {
rect.x = x; rect.y = y;
rect.h = h; rect.w = w;
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.tree.*;
public class ClearButton extends JButton implements Command {
Mediator med;
public ClearButton(ActionListener act, Mediator md) {
super("Clear");
setToolTipText("지우기");
addActionListener(act);
med = md;
}
public void Execute() {
med.clear();
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.tree.*;
public class RectButton extends JToggleButton implements Command {
Mediator med;
public RectButton(ActionListener act, Mediator md) {
super("Rectangle");
setToolTipText("사각형");
addActionListener(act);
med = md;
med.registerRectButton(this);
}
public void Execute() {
if (isSelected()) {
med.startRectangle();
}
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.tree.*;
public class UndoButton extends JButton implements Command {
Mediator med;
public UndoButton(ActionListener act, Mediator md) {
super("Undo");
setToolTipText("이전");
addActionListener(act);
med = md;
}
public void Execute() {
med.undo();
}
}
import java.awt.*;
public class visRectangle {
int x, y, w, h;
private Rectangle rect;
private 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) {
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();
}
}
public interface Command {
public void Execute();
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.tree.*;
public class JCanvas extends JPanel {
Mediator med;
public JCanvas(Mediator md) {
med = md;
med.registerCanvas(this);
setBackground(Color.white);
}
public void paint(Graphics g) {
super.paint(g);
med.reDraw(g);
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
public class JxFrame extends JFrame {
public JxFrame(String title) {
super(title);
setCloseClick();
setLF();
}
private void setCloseClick() {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
});
}
private void setLF() {
String laf = UIManager.getSystemLookAndFeelClassName();
try {
UIManager.setLookAndFeel(laf);
} catch (UnsupportedLookAndFeelException exc) {
System.err.println("Warning: UnsupportedLookAndFeel: " + laf);
} catch (Exception exc) {
System.err.println("Error loading " + laf + ": " + exc);
}
}
}
import java.awt.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.tree.*;
public class Mediator {
boolean startRect;
boolean rectSelected;
Vector drawings;
RectButton rect;
JPanel canvas;
visRectangle selectedRectangle;
private Caretaker caretaker;
public Mediator() {
startRect = false;
rectSelected = false;
drawings = new Vector();
caretaker = new Caretaker(drawings);
}
public void startRectangle() {
startRect = true;
}
public void createRect(int x, int y) {
unpick();
if (startRect) {
Integer count = new Integer(drawings.size());
caretaker.addElement(count);
visRectangle v = new visRectangle(x, y);
drawings.addElement(v);
startRect = false;
rect.setSelected(false);
canvas.repaint();
} else
pickRect(x, y);
}
public void registerRectButton(RectButton rb) {
rect = rb;
}
public void registerCanvas(JPanel p) {
canvas = p;
}
private void unpick() {
rectSelected = false;
if (selectedRectangle != null) {
selectedRectangle.setSelected(false);
selectedRectangle = null;
repaint();
}
}
public void rememberPosition() {
if (rectSelected) {
Memento m = new Memento(selectedRectangle);
caretaker.addElement(m);
repaint();
}
}
public void pickRect(int x, int y) {
visRectangle lastPick = selectedRectangle;
unpick();
for (int i=0; i<drawings.size(); i++) {
visRectangle v = (visRectangle)drawings.elementAt(i);
if (v.contains(x,y)) {
selectedRectangle = v;
rectSelected = true;
if (selectedRectangle != lastPick)
caretaker.rememberPosition(selectedRectangle);
v.setSelected(true);
repaint();
}
}
}
public void clear() {
drawings = new Vector();
caretaker.clear (drawings);
rectSelected = false;
selectedRectangle = null;
repaint();
}
private void repaint() {
canvas.repaint();
}
public void drag(int x, int y) {
if (rectSelected) {
if (selectedRectangle.contains(x, y)) {
selectedRectangle.move(x,y);
repaint();
}
}
}
public void reDraw(Graphics g) {
g.setColor(Color.black);
for (int i=0; i< drawings.size(); i++) {
visRectangle v = (visRectangle)drawings.elementAt(i);
v.draw(g);
}
}
public void undo() {
caretaker.undo ();
repaint();
}
public void removeDrawing(Object drawObj) {
drawings.removeElement(drawObj);
repaint();
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.tree.*;
public class MemDraw extends JxFrame
implements ActionListener {
JToolBar tbar;
Mediator med;
public MemDraw() {
super("Memento 패턴 Demo");
JPanel jp = new JPanel();
getContentPane().add(jp);
med = new Mediator();
jp.setLayout(new BorderLayout());
tbar = new JToolBar();
jp.add("North", tbar);
RectButton rect = new RectButton(this, med);
tbar.add(rect);
UndoButton undo = new UndoButton(this, med);
tbar.add(undo);
tbar.addSeparator();
ClearButton clr = new ClearButton(this, med);
tbar.add(clr);
JCanvas canvas = new JCanvas(med);
jp.add("Center", canvas);
MouseApp map = new MouseApp(med);
canvas.addMouseListener(map);
MouseMoveApp mvap = new MouseMoveApp(med);
canvas.addMouseMotionListener(mvap);
setSize(new Dimension(400,300));
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
Command comd = (Command)e.getSource();
comd.Execute();
}
static public void main(String[] argv) {
new MemDraw();
}
}
class MouseApp extends MouseAdapter {
Mediator med;
public MouseApp(Mediator md) {
super();
med = md;
}
public void mousePressed(MouseEvent e) {
med.createRect(e.getX(), e.getY());
}
public void mouseReleased(MouseEvent e) {
med.rememberPosition();
}
}
class MouseMoveApp extends MouseMotionAdapter {
Mediator med;
public MouseMoveApp(Mediator md) {
super();
med = md;
}
public void mouseDragged(MouseEvent e) {
med.drag(e.getX(), e.getY());
}
}


관련패턴
  • Command Pattern : memento를 실행취소가능한 operation에 대한 정보 유지에 사용
  • Iterator Pattern : Iterator사용시 Memento를 이용해 집합 object에 접근할 수 있다.

댓글 없음:

댓글 쓰기

블록체인 개요 및 오픈소스 동향

블록체인(block chain) 블록체인은 공공 거래장부이며 가상 화폐로 거래할때 발생할때 발생할 수 있는 해킹을 막는 기술. 분산 데이터베이스의 한 형태로, 지속적으로 성장하는 데이터 기록 리스트로서 분산 노드의 운영자에 의한 임의 조작이 불가...