본문 바로가기

Dev.../패턴자료

The Command Pattern

The Command Pattern

    Chain of Responsibility 패턴은 클래스들의 체인들을 따라서 요청들을 진행하지만, Command 패턴은 특별한 모듈로 하나의 요청을 진행한다. 그것은 하나의 객체 내부의 특정한 동작을 위한 하나의 요청을 둘러싸고 알려진 public 인터페이스를 준다. 그것이 수행하게 될 실제적인 동작에 대한 알고 있는 것 없이 요청들을 만들 수 있는 능력을 클라이언트에게 주게 한다. 그리고 어떤 방식으로든 클라이언트 프로그램에 영향을 주는 것 없이 동작을 변경할 수 있도록 한다.

Motivation

자바 사용자 인터페이스를 만들 때, 메뉴 항목들, 버튼들, 그리고 체크 박스와 같은 것들을 제공해야 하고, 프로그램이 무엇을 하는지를 사용자에게 알여 줘야 한다. 사용자가 이러한 컨트롤들 중의 하나를 선택했을 때, 프로그램은 하나의 ActionEvent를 받는다.
File|Open and File|Exit를 선택할 수 있는 메뉴항목과 버튼을 눌렀을 때 배경을 빨강색으로 바꿔주는 간단한 프로그램을 가정해 보자. 이 프로그램은 아래의 그림과 같다.

       

    이 프로그램은 mnuOpen 과 mnuExit를 MenuItem으로 가지는 File Menu로 구성되어 있다. 또 btnRed라 불리는 하나의 버튼을 포함한다. 어떤 것을 눌러도 우리는 다음 코드처럼 actionPerformed 메소드를 이용하여 이벤트를 잡아낼 수 있다.
	public void actionPerformed(ActionEvent e) {	Object obj = e.getSource();	if(obj == mnuOpen)		fileOpen();	//open file	if(obj == mnuExit)		exitClicked();	if(obj == btnRed)		redClicked();	//turn red}
    이 메소드가 부르는 3개의 private 메소드는 :
private void exitClicked() {	System.exit(1);}//-----------------------------------------private void fileOpen() {	FileDialog fDlg = new FileDialog(this, "Open a file", FileDialog.LOAD);	fDlg.show();}//-----------------------------------------private void redClicked() {	p.setBackground(Color.red);}
    몇개 안되는 메뉴 항목과 버튼들이 있을 때는 이러한 방식도 괜찮지만, 메뉴 항목이 수 십개가 되고 여러개의 버튼을 가지고 있을 때는 actionPerformed 코드는 꾀나 거대해지게 된다.

The Command Pattern

모든 객체가 자신의 명령을 직접적으로 받게 하는 한가지 방법은 Command 객체를 사용하는 것이다. Command 객체는 항상 액션이 객체내에서 발생했을 때 호출되는 Execute() 메소드를 가지고 있다.   
가장 간단하게, Command 객체는 적어도 다음의 인터페이스를 구현한다.
public interface Command {	public void Execute();}
이 인터페이스를 사용하는 목적은 actionPerformed 메소드를 다음과 같이 줄이기 위해서이다.
public void actionPerformed(ActionEvent e) {	Command cmd = (Command)e.getSource();	cmd.Execute();}
그러면 우리는 요구되는 엑션을 수행하는 각각의 객체에 대한 Execute 메소드를 제공할 수 있어, 그 것이 속해 있는 객체내에서 무엇을 할 것인가에 대한 정보를 유지할 수 있다.

    Command 패턴의 중요한 목적은 프로그램을 유지하고 사용자 인터페이스 객체들을 완전히 분리하는 것이고 다른 객체들의 작업에 대해 알 필요가 없다. 사용자 인터페이스는 명령을 받고 Command 객체에게 어떤 종류의 의무든지 알려줄 수가 있다. 사용자 인터페이스는 어떤일을 해야할 지 알려고 하지 않아도 되고 알 필요도 없다.

    Command 객체는 또 리소들이 바로 사용되지 않을 때 명령을 수행하는 프로그램에게 알려 줄 필요가 있을 때 사용되어 질 수 있다. 이러한 경우에서는 나중에 실행되는  queuing 명령들이다.
마지막으로, 취소 요구들을 지원할 수 있기 위해서 연산자들을 기억하는 Command 객체에도 사용되어 질 수 있다.

Building Command Objects

    이와 같은 프로그램을 위한 Command 객체들을 만드는 거에 관한 여러가지 방법이 있고 각각의 방법은 몇가지 이점을 갖는다. 우리는 가장 간단한 방법부터 시작할 것이다 : MenuItem 과 Button 클래스에서 파생 받아 새로운 클래스를 만들고 각각 Command 인터페이스를 구현한다.  
class btnRedCommand extends Button implements Command {	public btnRedCommand(String caption) {		super(caption);	//initialize the button	}	public void Execute() {		p.setBackground(Color.red);	}	//----------------------------------------	class fileExitCommand extends MenuItem implements Command {	public fileExitCommand(String caption) {		super(caption);	//initialize the Menu	}	public void Execute() {		System.exit(0);	}
    이 방법은 확실히 actionPerformed 메소드에서 만들어진 것을 간단하게 호출할 수 있지만, 실행하고자 하는 각각의 동작을 위하여 새로운 크래스를 만들고 인스턴스화 하는 것을 요구한다.
	mnuOpen.addActionListener(new fileOpen());	mnuExit.addActionListener(new fileExit());	btnRed.addActionListener(new btnRed());
    우리는 이러한 클래스들에 필요한 파라미터드을 전달하는 대부분의 문제를 그것들의 내부 클래스를 만들어 우회할 수 있다. 이것은 직접 이용할 수 있는 Panel 과 Frame 과 같은 객체들을 만든다.

    그러나, 내부의 클래스들은 명령들이 급증함에 따라 좋은 생각은 아니다. 왜냐면, 어떤 다른 사용자 인터페이스 컴포넌트에 접근하는 그것들 중의 어떤것도 메인 클래스 내부에서 유지해야 하기 때문이다. 이것이 혼란스러운 작은 내부 클래스의 증가로 메인 클래스에 대한 코드는 복잡해 진다.

    물론, 이러한 클래스들에게 필요한 파라미터들을 전달하는 것을 꺼려하지 않는다면 그것들은 독립적으로 될 수 있다. 여기서 우리는 파라미터로 Frame 객체와 Panel 객체를 전달하였다.
	mnuOpen = new fileOpenCommand("Open...", this);	mnuFile.add(mnuOpen);	mnuExit = new fileExitCommand("Exit");	mnuFile.add(mnuExit);	p = new Panel();	btnRed = new btnRedCommand("Red", p);	p.add(btnRed);
두번째 경우에서 우리의 메뉴와 버튼 command 클래스들은 메인 클래스 외부에 존재할 수 있고, 원한다면 파일을 나누어 저장할 수 도 있다.

The Command Pattern in Java

그러나 여전히 이 패턴을 접근하는 두어가지 방법이 있다. 만약 모든 컨트롤을 그 자신의 actionListener 클래스에 주었다면, 각각의 command 객체들을 효과적으로 생성한 것이다. 그리고, 사실, 이것이 자바1.1 이벤트 모델의 설계자들에게 있어서는 실제적이다.
    이 방법을 구현하기 위해 우리는 ActionListener 클래스를 구현한 작은 클래스들을 생성한다.
class btnRed implements ActionListener {	public void actionPerformed(ActionEvent e) {		p.setBackground(Color.red);	}}	//------------------------------------------class fileExit implements ActionListener {	public void actionPerformed(ActionEvent e) {		System.exit(0);	}}
그리고 일반적으로 리스너로써 그것들을 등록한다.
	mnuOpen.addActionListener(new fileOpen());	mnuExit.addActionListener(new fileExit());	btnRed.addActionListener(new btnRed());

Consequences of the Command Pattern

Command 패턴의 주요 단점은 메인 클래스를 어지럽게 하는 작은 클래스들의 급증이다.

'Dev... > 패턴자료' 카테고리의 다른 글

The Memento Pattern  (0) 2005.03.01
The State Pattern  (0) 2005.03.01
The Iterator Pattern  (0) 2005.03.01
The Interpreter Pattern  (0) 2005.03.01
The Strategy Pattern  (0) 2005.03.01