Command 패턴
패키지 |
이름 |
해설 |
command |
Command |
명령을 표현하는 인터페이스 |
MacroCommand |
복수의 명령을 모은 명령을 표현하는 클래스 | |
drawer |
DrawCommand |
점 그리기 명령을 표현하는 클래스 |
Drawable |
그리기 대상을 표현하는 인터페이스 | |
DrawCanvas |
그리기 대상을 구현하는 클래스 | |
Anonymous |
Main |
동작 테스트용 클래스 |
Command 패턴의 핵심은 명령을 시키는 사람과 명령을 구체적인 계획을 가지고 수행하는 사람을 분리시키는 것입니다.
문서열기나 복사하기나 취소하기 는 셋다 명령입니다. 그리고 명령을 수행시키는 사람은 따로 있습니다.
한번 어떻게 분리시켰는지 소스를 보고 실행해봅시다.
Command 인터페이스
package command;
public interface Command {
public abstract void execute();
}
MacroCommand 클래스
package command;
import java.util.Stack;
import java.util.Iterator;
public class MacroCommand implements Command {
// 명령의 집합
private Stack commands = new Stack();
// 실행
public void execute() {
Iterator it = commands.iterator();
while (it.hasNext()) {//모든 명령을 실행 여기서 이전에 그렸던 모든 것을 다시 그리게 됨.
((Command)it.next()).execute();//execute는 DrawCommand의 execute메소드를 호출할 것임.
}
}
// 추가
public void append(Command cmd) {
if (cmd != this) {
commands.push(cmd);//스택을 쌓음
}
}
// 최후의 명령을 삭제
public void undo() {
if (!commands.empty()) {
commands.pop();
}
}
// 전부 삭제
public void clear() {
commands.clear();
}
}
Drawable 인터페이스
package drawer;
public interface Drawable {
public abstract void draw(int x, int y);
}
DrawCanvas 클래스
package drawer;
import command.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* @author kch
*/
public class DrawCanvas extends Canvas implements Drawable {
// 그림 그리는 색
private Color color = Color.red;
// 그림 그리는 점의 반경
private int radius = 6;
// 이력
/**
* @uml.property name="history"
* @uml.associationEnd
*/
private MacroCommand history;
// 생성자
public DrawCanvas(int width, int height, MacroCommand history) {
setSize(width, height);
setBackground(Color.white);
this.history = history;
}
// 이력 전체를 다시 그리기 fillOval이 실행되면 이 메소드가 자동적으로 호출됨.
public void paint(Graphics g) {
history.execute();//MacroCommand의 execute가 실행됨
//즉 모든 명령어가 실행됨.
}
// 그림 그리기, x, y좌표에 점을 찍음
public void draw(int x, int y) {
Graphics g = getGraphics();
g.setColor(color);
g.fillOval(x - radius, y - radius, radius * 2, radius * 2);
}
}
DrawCommand 클래스
package drawer;
import command.Command;
import java.awt.Point;
/**
* @author kch
*/
public class DrawCommand implements Command {
// 그림 그리기 대상
/**
* @uml.property name="drawable"
* @uml.associationEnd
*/
protected Drawable drawable;
// 그림 그리기 위치
private Point position;
// 생성자
public DrawCommand(Drawable drawable, Point position) {
this.drawable = drawable;
this.position = position;
}
// 실행
public void execute() {
drawable.draw(position.x, position.y);//x,y 포지션에 그림.
//drawable 인스턴스는 DrawCanvas이므로 DrawCanvas의 draw메소드가 실행됨
}
}
Main 클래스
import command.*;
import drawer.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* @author kch
*/
public class Main extends JFrame implements ActionListener, MouseMotionListener, WindowListener {
// 그림 그린 이력
/**
* @uml.property name="history"
* @uml.associationEnd
*/
private MacroCommand history = new MacroCommand();
// 그림 그리는 영역
/**
* @uml.property name="canvas"
* @uml.associationEnd
*/
private DrawCanvas canvas = new DrawCanvas(400, 400, history);
// 제거 버튼
private JButton clearButton = new JButton("clear");
// 생성자
public Main(String title) {
super(title);
this.addWindowListener(this);
canvas.addMouseMotionListener(this);
clearButton.addActionListener(this);
Box buttonBox = new Box(BoxLayout.X_AXIS);
buttonBox.add(clearButton);
Box mainBox = new Box(BoxLayout.Y_AXIS);
mainBox.add(buttonBox);
mainBox.add(canvas);
getContentPane().add(mainBox);
pack();
show();
}
// ActionListener용
public void actionPerformed(ActionEvent e) {
if (e.getSource() == clearButton) {
history.clear();
canvas.repaint();
}
}
// MouseMotionListener용
public void mouseMoved(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
Command cmd = new DrawCommand(canvas, e.getPoint());//초기화할때 canvas를 넘겨줌으로써
//drawable참조변수가 canvas 인스턴스를 가리키게됨.
history.append(cmd);
cmd.execute();//DrawCommand의 execute 메소드가 실행됨.
}
// WindowListener용
public void windowClosing(WindowEvent e) {
System.exit(0);
}
public void windowActivated(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
public static void main(String[] args) {
new Main("Command Pattern Sample");
}
}
결과화면
여기서는 명령은 2가지 종류인데 하나는 지금까지 수행한 명령들을 다 저장하는 것과 다른 하나는 그림판에 그림을 그리는 명령입니다.
그리고 명령을 하는 사람은 Main 클래스에서 public void mouseDragged(MouseEvent e) 부분이구요.
자 그럼 차근차근 생각해봅시다.
처음에 명령이 내려졌습니다. canvas에 빨간 점을 한 개 찍으라는 명령입니다.
이 프로그램의 특징은 명령이 내려지면 무조건 그것을 저장하고 그리고 지금까지 저장한 모든 점을 화면에 찍게됩니다.
그러면
Command cmd = new DrawCommand(canvas, e.getPoint());//초기화할때 canvas를 넘겨줌으로써
//drawable참조변수가 canvas 인스턴스를 가리키게됨.
history.append(cmd);
cmd.execute();//DrawCommand의 execute 메소드가 실행됨.
위의 append라는 메소드를 통해서 일단 현재 점 찍는 위치는 저장해둡니다.(명령 첫번째)
그리고 cmd.execute라는 메소드로 점을 실제로 찍게되는데 이때 점을 찍으라고 시키는
놈과 찍는 놈은 분리시켜놨습니다.
시키는놈은 DrawCommand 찍는놈은 DrawCanvas입니다. 그래서 결국 점을 찍게되는데
점을 찍을 때 이전에 저장해둔 점도 다찍기 위해서 MacroCommand를 이용해서 점을
다 찍게됩니다.
이해가 안된다면 다음줄만 이해하시면 이 패턴은 끝난겁니다.
기본적으로 Command 패턴은 명령을 하는놈과 명령을 수행하는 놈이 나눠져있습니다.
그리고 명령을 하는놈은 무조건 execute만 외쳐주면 알아서 수행하는 놈이 하게됩니다.
그리고 명령은 종류가 많을 수 있지만 명령 하는놈은 execute만 외치면 그때그때
상황에 맞는 것을 알아서 수행하게 됩니다.
'프로그래밍 > 자바 디자인패턴' 카테고리의 다른 글
디자인패턴 Interpreter 패턴 (0) | 2012.08.27 |
---|---|
디자인패턴 Proxy 패턴 (0) | 2012.08.16 |
디자인패턴 Flyweight 패턴 (0) | 2012.08.15 |
디자인패턴 State 패턴 (0) | 2012.08.14 |
디자인 패턴 Memento 패턴 (0) | 2012.08.13 |
디자인패턴 Observer 패턴 (0) | 2012.08.10 |
디자인패턴 Mediator 패턴 (0) | 2012.08.09 |
디자인패턴 Facade 패턴 (0) | 2012.08.06 |