Chapter 22. Command : 명령을 클래스로 만든다

1. 예제 프로그램 - 간단한 그림 그리기 프로그램
<Command.java>

package ch22.Sample.command;

// ‘명령’을 표현하기 위한 인터페이스
public interface Command {
	// 무언가를 실행하는 메소드
	// 구체적으로 무슨 일을 하는지는 
	// Command 인터페이스를 구현한 클래스가 결정한다.
	public abstract void execute();
}

<DrawCommand.java>

package ch22.Sample.drawer;

import ch22.Sample.command.Command;
import java.awt.Point;

// ‘그림 그리기 명령’을 표현함
public class DrawCommand implements Command {
	// 그림 그리기를 실행할 대상(객체)를 저장함
	protected Drawable drawable;

	// 그림 그리는 위치
	private Point position;
	// java.awt.Point 클래스: X좌표와 Y좌표를 갖는 클래스
	// 2차원 평면 상의 위치를 나타냄
	// 속성으로 x, y를 가진다.

	// 생성자
	public DrawCommand(Drawable drawable, Point position) {
		this.drawable = drawable;
		this.position = position;
	}

	// 실제 그리기를 실행하는 메소드
	// drawable 필드의 draw 메소드를 호출함
	public void execute() {
		drawable.draw(position.x, position.y);
	}
}

<MacroCommand.java>

package ch22.Sample.command;

import java.util.Stack;
import java.util.Iterator;

// ‘여러 개의 명령을 한데 모은 명령’을 나타냄
// Composite 패턴이 사용됨
// => 여러 개의 명령을 모은 것(container)이면서, 
// 그 자체가 하나의 명령(content)가 된다.
public class MacroCommand implements Command {
	// 다수의 Command를 모아둠
	// Stack: 나중에 들어간 원소가 먼저 나오는 자료구조체
	private Stack commands = new Stack();

	// 실행
	public void execute() {
		// 자신이 가지고 있는 모든 명령의 execute( )을 호출한다(실행한다)
		Iterator it = commands.iterator();
		while (it.hasNext()) {
			((Command) it.next()).execute();
		}
		// 자신이 가지고 있는 명령이 MacroCommand이면, 
		// 그 MacroCommand가 가지고 있는 명령들의 execute( )이 차례대로 실행된다.
		// (recursive call)

	}

	// MacroCommand 클래스에 
	// 새로운 Command(Command 인터페이스를 구현한 클래스의 인스턴스)를 
	// 추가하는 메소드
	public void append(Command cmd) {
		// 실수로 자기 자신을 추가하지 않도록 체크함
		// 자기 자신이 추가되면, execute( ) 실행 시 무한 루프가 돌게 된다.
		if (cmd != this) {
			commands.push(cmd); // Stack 클래스의 push( )를 이용함
		}
	}

	// commands의 마지막 명령을 삭제하는 메소드
	public void undo() {
		if (!commands.empty()) {
			commands.pop();
		}
	}

	// commands 의 모든 명령을 삭제하는 메소드
	public void clear() {
		commands.clear();
	}
}

<Drawable.java>

package ch22.Sample.drawer;

// ‘그림 그리기 대상’을 표현함
public interface Drawable {
    public abstract void draw(int x, int y);
}

<DrawCanvas.java>

package ch22.Sample.drawer;

import ch22.Sample.command.*;

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

// Drawable 인터페이스를 구현하고, java.awt.Canvas 를 상속함
public class DrawCanvas extends Canvas implements Drawable {
	// 그림 그리는 색
	private Color color = Color.red;

	// 그림 그리기를 할 점의 변경
	private int radius = 6;

	// 지금까지 실행한 그림 그리기 명령어들의 집합을 가지고 있음
	private MacroCommand history;

	// 폭, 높이, 그림 그리기 이력(history)를 받아서,
	// DrawCanvas 인스턴스를 초기화한다.
	public DrawCanvas(int width, int height, MacroCommand history) {
		setSize(width, height);
		setBackground(Color.white);
		this.history = history;
	}

	// DrawCanvas를 다시 그릴 필요가 생겼을 때
	// java.awt 프레임워크로부터 자동으로 호출되는 메소드
	// repaint( ) 메소드가 호출되면, 화면이 지워진 후 자동으로 paint( ) 메소드가 실행된다.
	public void paint(Graphics g) {
		// history가 보관하고 있는 모든 그리기 명령들을 실행한다.
		history.execute();
	}

	// 그리기
	public void draw(int x, int y) {
		// Graphics 객체를 얻어서,
		Graphics g = getGraphics();
		// 색깔을 빨간색으로 지정하고
		g.setColor(color);
		// Graphics 객체의 filloval(x, y, 사각형 가로, 사각형 세로)을 
		// 이용하여 원을 그린다.
		g.fillOval(x - radius, y - radius, radius * 2, radius * 2);
	}
}

<Main.java>

package ch22.Sample;

import ch22.Sample.command.*;
import ch22.Sample.drawer.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

// 예제 프로그램을 작동시키는 클래스
public class Main extends JFrame implements ActionListener,
		MouseMotionListener, WindowListener {

	// DrawCanvas 생성 시에 인자로 넘겨줌
	// Main 인스턴스와 DrawCanvas 인스턴스가 history를 공유한다.
	private MacroCommand history = new MacroCommand();

	// 그림 그리는 영역을 나타냄
	private DrawCanvas canvas = new DrawCanvas(400, 400, history);

	// javax.swing.JButton 클래스
	// 그린 점들을 모두 지우는 버튼
	private JButton clearButton = new JButton("clear");

	public Main(String title) {
		super(title);
		// 리스너 등록하기
		this.addWindowListener(this);
		canvas.addMouseMotionListener(this);
		clearButton.addActionListener(this);

		// 여러 가지 GUI 부품을 배치함
		// Box 객체를 이용함
		// Box: BoxLayout 객체를 레이아웃매니저로 사용하는 가벼운 컨테이너
		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();
		setVisible(true);
	}

	// clearButton이 눌러졌을 때 호출되는 메소드
	
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == clearButton) {
			history.clear(); // history에 보관되어 있던 모든 명령을 지우고
			canvas.repaint(); // 캔버스의 repaint( )가 호출된다.
		}
	}

	// MouseMotionListener
	public void mouseMoved(MouseEvent e) {
	}

	// 사용자가 마우스를 drag 하면 이 메소드가 호출된다.
	public void mouseDragged(MouseEvent e) {
		// 그리기 명령을 나타내는 DrawCommand 객체를 생성한 후,
		Command cmd = new DrawCommand(canvas, e.getPoint());  // e.getPoint( ) : 마우스 이벤트가 발생한 위치를 얻음
		history.append(cmd); // 이를 history에 추가하고,
		cmd.execute(); // 지정 위치에 빨간 점을 그린다.
	}

	// WindowListener
	
	// 창의 오른 쪽 위 아이콘 중에  X 사각형을 눌렀을 때 호출되는 메소드
	public void windowClosing(WindowEvent e) {
		System.exit(0);
	}

	// WindowListener 인터페이스에 선언되어 있는 나머지 메소드들
	// 구현할 필요가 없으므로, 빈 문장의 메소드로 구현되어 있다.
	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. 연습문제 3번 - 어댑터 사용
<Command.java>

package ch22.Sample.command;

// ‘명령’을 표현하기 위한 인터페이스
public interface Command {
	// 무언가를 실행하는 메소드
	// 구체적으로 무슨 일을 하는지는 
	// Command 인터페이스를 구현한 클래스가 결정한다.
	public abstract void execute();
}

<DrawCommand.java>

package ch22.A3.drawer;

import ch22.A3.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);
    }
}

<MacroCommand.java>

package ch22.A3.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();
        }
    }
    // 추가
    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.java>

package ch22.A3.drawer;

public interface Drawable {
    public abstract void draw(int x, int y);
}

<DrawCanvas.java>

package ch22.A3.drawer;

import ch22.A3.command.*;

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class DrawCanvas extends Canvas implements Drawable {
    // 그림 그리는 색
    private Color color = Color.red;
    // 그림 그리기를 할 점의 반경
    private 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);
    }
}

<Main.java>

package ch22.A3;
import ch22.A3.command.*;
import ch22.A3.drawer.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

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");

    // 생성자
    public Main(String title) {
        super(title);

        this.addWindowListener(new WindowAdapter() {        
            public void windowClosing(WindowEvent e) {      
                System.exit(0);                             
            }                                               
        });                                                 
        canvas.addMouseMotionListener(new MouseMotionAdapter() {    
            public void mouseDragged(MouseEvent e) {                
                Command cmd = new DrawCommand(canvas, e.getPoint());    
                history.append(cmd);                                    
                cmd.execute();                                          
            }                                                           
        });                                                             
        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();
        setVisible(true);
    }

    // ActionListener용
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == clearButton) {
            history.clear();
            canvas.repaint();
        }
    }

    public static void main(String[] args) {
        new Main("Command Pattern Sample");
    }
}

좋은 웹페이지 즐겨찾기