명령을 명세하고 있는 클래스를 만들어 호출될 때 마다 명령을 기록해두었다가 재사용하기도 하고 관리하고 싶을 때 사용하면 되는 패턴입니다.
흔히 이벤트를 자주 사용하는 GUI, app에 사용되기도 합니다.
파일 구조
UML
Command.java
package command; public interface Command{ public abstract void execute(); }
어떤 클래스가 Command Interface를 구현하여 실행하냐에 따라 다르지만 execute는 무언가를 실행하는 메소드입니다.
MacroCommand.java
package command; import java.util.Stack; import java.util.Iterator; public class MacroCommand implements Command{ private Stack<Command> commands = new Stack<>(); public void execute(){ Iterator<Command> it = commands.iterator(); while(it.hasNext()) it.next().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(); } }
MacroCommand는 가장 최근에 실행된 명령들을 저장하는 클래스입니다.
undo 메소드는 가장 최근에 실행된 메소드를 제거하는 함수입니다.
execute메소드는 저장된 명령들을 다시 실행시키는 함수이다.
append는 명령을 추가한다.
Drawable.java
package drawer; public interface Drawable{ public abstract void draw(int x, int y); }
그림을 그리는 명령을 명세하고 있다.
DrawCanvas.java
package drawer; import command.*; import java.awt.*; public class DrawCanvas extends Canvas implements Drawable{ private final Color color = Color.red; private final int RADIUS = 6; private MacroCommand history; public DrawCanvas(int width, int height, MacroCommand history){ setSize(width, height); setBackground(Color.white); this.history = history; }
public void paint(Graphics g){ history.execute(); }
public void draw(int x, int y){ Graphics g = getGraphics(); g.setColor(color); g.fillOval(x-RADIUS, y-RADIUS, RADIUS*2, RADIUS*2); } }
canvas를 만들어주는 클래스이다. 실질적으로 클릭이 될때 마다 원이 생기게 만들어 준다.
paint(Graphics g)메소드는 repaint될 때마다 지운 후 다시 호출되어 실행된다. 그래서 clear로 모두 지우면 stack에 남아 있는 명령이 없기 때문에 아무것도 보이지 않는다.
undo는 바로 직전의 명령을 지우기 때문에 repaint해줘도 stack에 남아있다. 그래서 reapaint를 해주어도 다시 보여준다.
DrawCommand.java
package drawer; import command.Command; import java.awt.Point; public class DrawCommand implements Command{ 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); } }
DrawCommand는 구체적으로 명령을 실행하는 클래스이다. canvas에 그림을 그려주라고 명령을 한다.
Main.java
import command.Command; import command.MacroCommand; import drawer.DrawCanvas; import drawer.DrawCommand; import javax.swing.*; import java.awt.event.*; public class Main extends JFrame implements ActionListener{
private MacroCommand history = new MacroCommand(); private DrawCanvas canvas = new DrawCanvas(400, 400, history); private JButton clearButton = new JButton("clear"); private JButton undoButton = new JButton("undo"); public Main(String title){ super(title); this.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { super.windowClosed(e); System.exit(0); } }); canvas.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { Command command = new DrawCommand(canvas, e.getPoint()); history.append(command); command.execute(); } }); clearButton.addActionListener(this); undoButton.addActionListener(this); Box buttonBox = new Box(BoxLayout.X_AXIS); buttonBox.add(clearButton); buttonBox.add(undoButton); Box mainBox = new Box(BoxLayout.Y_AXIS); mainBox.add(buttonBox); mainBox.add(canvas); getContentPane().add(mainBox); pack(); show(); }