프로그래밍/자바 디자인패턴

디자인패턴 Command 패턴

가카리 2012. 8. 18. 21:55
반응형

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 DrawCommandexecute메소드를 호출할 것임.

        }                              

    }

    // 추가

    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();//MacroCommandexecute 실행됨

                                                               //즉 모든 명령어가 실행됨.

    }

    // 그림 그리기, 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이므로 DrawCanvasdraw메소드가 실행됨

    }                                    

 }

 

 

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만 외치면 그때그때

 

상황에 맞는 것을 알아서 수행하게 됩니다.