개발/Java
디자인 패턴 - Command Pattern
haloper
2016. 3. 29. 14:30
커맨드 패턴의 주요 목적은
작업을 요청하는 쪽과 작업을 처리하는 쪽을 분리하여
서로 의존관계가 없도록 하는 것 입니다.
(서로 상대방의 존재 여부를 알지도, 알 필요도 없습니다.)
또한, 커맨드 패턴 적용 시
연속된 작업의 이력 관리와
작업 취소 로직을 쉽게 적용할 수 있습니다.
샘플 코드를 작성하기에 앞서
기본적인 용어를 정리하도록 하겠습니다.
Invoker : 작업을 요청하는 객체
Receiver : 작업을 처리하는 객체
Command : 작업 클래스들이 구현해야 하는 인터페이스
간단하게 원과 네모를 그리는 로직으로 샘플을 구현해 보겠습니다.
x,y 좌표를 가진 원과 네모를 그리거나 지우는 작업을 합니다.
작업 이력을 stack에 넣어 보관하고,
필요시 undo 할 수도 있습니다.
우선 Command Interface를 작성하겠습니다.
* Command.java
public interface Command { public void execute(); public void undo(); }
Command 인터페이스를 구현하는 Command 클래스들 입니다.
각 클래스의 인스턴스가 하나의 작업을 나타냅니다.
* Command Class
//DrawCircleCommand.java public class DrawCircleCommand implements Command { private CircleReceiver circle; public DrawCircleCommand(CircleReceiver circle) { this.circle = circle; } @Override public void execute() { circle.Draw(); } @Override public void undo() { circle.Erase(); } } //EraseCircleCommand.java public class EraseCircleCommand implements Command { private CircleReceiver circle; public EraseCircleCommand(CircleReceiver circle) { this.circle = circle; } @Override public void execute() { circle.Erase(); } @Override public void undo() { circle.Draw(); } } //DrawRectCommand.java public class DrawRectCommand implements Command { private RectReceiver rect; public DrawRectCommand(RectReceiver rect) { this.rect = rect; } @Override public void execute() { rect.Draw(); } @Override public void undo() { rect.Erase(); } } //EraseRectCommand.java public class EraseRectCommand implements Command { private RectReceiver rect; public EraseRectCommand(RectReceiver rect) { this.rect = rect; } @Override public void execute() { rect.Erase(); } @Override public void undo() { rect.Draw(); } }
작업 내용을 알고 있는,
실제로 작업을 처리할 Receiver 클래스를 만들어 보겠습니다.
각 작업에 대한 실제 로직이 들어 있습니다.
* Receiver Class
//CircleReceiver.java public class CircleReceiver { private int x,y; public CircleReceiver(int x, int y) { this.x = x; this.y= y; } public void Draw() { System.out.println(String.format("원을 그립니다. (%d,%d)", x, y)); } public void Erase() { System.out.println(String.format("원을 지웁니다. (%d,%d)", x, y)); } } //RectReceiver.java public class RectReceiver { int x,y; public RectReceiver(int x, int y) { this.x = x; this.y= y; } public void Draw() { System.out.println(String.format("네모를 그립니다. (%d,%d)", x, y)); } public void Erase() { System.out.println(String.format("네모를 지웁니다. (%d,%d)", x, y)); } }
작업을 처리할 모든 준비가 되었습니다.
이제, 작업을 요청하는 Invoker 클래스를 만들겠습니다.
* DrawingInvoker.java
public class DrawingInvoker { private Stack<Command> commandStack; public DrawingInvoker() { commandStack = new Stack<Command>(); } public void execute(Command command) { commandStack.push(command); command.execute(); } public void undo() { commandStack.pop().undo(); } }
마지막으로 위 패턴이 잘 동작하는지 확인할
Cilent Class 를 만들어서 확인해 보도록 하겠습니다.
* Client.java
public class Client { public static void main(String[] args) { DrawingInvoker drawing = new DrawingInvoker(); //원을 그리고 drawing.execute(new DrawCircleCommand(new CircleReceiver(3,5))); //원을 그립니다. (3,5) //네모를 그리고 RectReceiver rect = new RectReceiver(12,3); drawing.execute(new DrawRectCommand(rect)); //네모를 그립니다. (12,3) //네모를 지웠다가 다시 그리고 drawing.execute(new EraseRectCommand(rect)); //네모를 지웁니다. (12,3) drawing.execute(new DrawRectCommand(rect)); //네모를 그립니다. (12,3) //원을 그린다 drawing.execute(new DrawCircleCommand(new CircleReceiver(1,1))); //원을 그립니다. (1,1) //잘못 그렸네.. 작업 취소 drawing.undo(); //원을 지웁니다. (1,1) drawing.undo(); //네모를 지웁니다. (12,3) drawing.undo(); //네모를 그립니다. (12,3) //네모를 다시 그림 drawing.execute(new DrawRectCommand(rect)); //네모를 그립니다. (12,3) } }