객체에 대한 데이터를 저장하고 복원할 수 있게 하는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에 접근할 수 있다.
'Programming > Design Pattern' 카테고리의 다른 글
[펌] The Iterator Pattern (0) | 2006.01.21 |
---|---|
[펌] The Mediator Pattern (0) | 2006.01.21 |
[펌] The Facade Pattern (0) | 2006.01.21 |
[펌] The Flyweight Pattern (0) | 2006.01.21 |
[펌] Chapter 5 Behavioral Patterns(행위 패턴) (0) | 2006.01.21 |