CS/디자인패턴

[디자인패턴] Command 패턴 (커맨드 패턴, 액션 패턴, 트랜잭션 패턴)

mabb 2023. 6. 25. 16:02
반응형

▶디자인 패턴 ( Design Pattern)

:프로그램 개발 시 문제 해결을 위하여 빈번히 사용되는 개발자들의 경험, 내적인 축적에 대하여,
GoF(Gang of Four) 라 불리는 4인의 개발자들이 각각을 패턴으로 정의하고 이름을 붙였다.
이를 디자인 패턴 (Design Pattern) 이라고 한다.
 23개의 디자인 패턴을
『Elements of Reusable Object-Oriented Software』
라는 책으로 발간하였다.

Gang of Four 분들은 모두 안경을 썼다.

 

▷디자인 패턴의 용어를 빌리면 서로의 아이디어를 보다 용이하게 비교, 논의할 수 있게 된다.
▷재사용과 기능확장이 쉬운 소프트웨어를 만들기 위한 유익한 기법이 바로 디자인 패턴이다.

 

---------------------------------------------------------
1. Iterator
2. Adapter
3. Template Method
4. Factory Method
5. Singleton
6. Prototype
7. Builder
8. Abstract Factory
9. Bridge
10. Strategy
11. Composite
12. Decorator
13. Visitor
14. Chain of Responsibility
15. Facade
16. Mediator
17. Observer
18. Memento
19. State
20. Flyweight
21. Proxy
22. Command
23. Interpreter
---------------------------------------------------------

▶Command 패턴

"명령 그 자체를 클래스로 표현"
"Command는 이벤트 구동 프로그래밍에서 Event와 같다"
"명령에 대한 정보와 실행을 가진 객체"
"Command객체는 리스너(리시버)가 실제적인 처리"
"명령 그 자체를 객체화 하였기 때문에 명령 집합을 저장하여 재실행 및 undo에 용이"


-왜 사용하는가
-클래스다이어그램
-각 클래스(인터페이스)의 역할
-Java소스

▶Command 패턴에 대한 이해

-사용자 조작 등 어떠한 사건이 발생했을 때 특정 기능이 동작하는 경우가 있다. 이때 특정 기능의 동작은 클래스 자기 자신이나 다른 클래스의 '메서드를 호출하는 것'으로 수행한다. '사건이 발생하면 메서드 호출' 같은 경우에는 해당 메서드를 호출한 이력은 어딘가에 저장되지 않고 휘발된다. 특정 기능 수행에 대한 데이터와 기능 수행 동작 자체를 객체로 만든다면 이 객체를 저장하여 '명령' 그 자체를 재사용할 수 있게 된다.

-Command는 명령을 의미한다. 명령 그 자체를 클래스로 표현하는 것을 Command패턴이라고 하는데 이벤트 구동 프로그래밍에서의 Event - Listner 중 Event에 해당한다고 볼 수 있다.

-명령 그 자체를 의미하는 인스턴스의 집합을 관리하면 명령의 재이용에 용이해진다.

-명령(명령에 대한 요청)을 클래스로 표현하는 것이 Command 패턴이다.

-명령을 명령에 대한 모든 정보가 포함된 독립 객체로 만드는 디자인 패턴이다.

-명령을 객체로 다루면, 명령을 대기열에 넣을 수 있고 재실행 및 취소할 수 있다.

-명령을 객체로 다룰 때 보통 Stack 구조를 사용한다.

-이벤트 구동 프로그램의 Event와 Command는 같은 의미이다.

-클라이언트에 의해 이벤트가 발생했을 때 이벤트리스너가 동작한다. 마찬가지로 명령 객체가 실행될 때 Receiver가 동작한다.

-Command는 명령에 대한 정보와 실행(excute)을 가지고 있는 객체이다.

-Client는 특정 사건이 생겼을 때 Command를 생성한다.

-Command를 실행하는 역할을 Invoker라고 칭한다.

-Command 의 excute에 대한 실행은 명령의 대상이 되는 Receiver가 행한다.
excute(){... receiver.action();}

-Command 객체를 저장하여 이력을 저장하는 역할을 MacroCommand라 칭한다.

-Command와 MacroCommand는 Coposit 패턴으로 만들어진다.

-Command를 Event라고도 칭한다.

-Event(사건) 그 자체를 인스턴스로 다루어 관리하고 순서대로 처리해 나갈 수 있다.

 

▶왜 사용하는가

보통 GUI의 이벤트 구동 프로그래밍에서 Event e처럼 리스너의 동작을 위한 명령(요청)을 객체화하는 경우를 생각해 볼 수 있다. 명령에 대한 정보와 실행 그 자체를 객체화하면 명령을 재이용하고 관리하기 용이하다.

 

▶클래스다이어그램

Command 패턴의 클래스다이어그램

 

▶각 클래스(인터페이스)의 역할

▷Command 역할 - Command.java

명령의 실행을 선언한다. (execute()). 명령의 이력을 관리할 때 MacroCommand와 ConcreteCommand를 동일시할 수 있도록 한다.

▷ConcreteCommand 역할 - ConcreteCommand.java

명령 객체를 생성하는 역할을 한다. 명령의 실행을 Receiver에게 위임한다.

▷Receiver 역할 - Receiver.java

명령에 대한 처리를 선언한다. (action() )

▷ConcreteReceiver 역할 - ConcreteReceiver.java

명령에 대한 구체적인 처리를 한다.

▷Invoker 역할 - Invoker.java

클라이언트에서 사건 발생 시, Receiver를 생성자 주입한 Command 객체를 생성하고 Command를 excute 하는 역할을 한다.

 

▶Java 소스

*java1.8 이전 버전을 사용하는 교재를 참고하였습니다.
Java API 문서 등을 참조하여 제네릭을 적용하는 등 소스를 개선할 필요가 있습니다.
(https://docs.oracle.com/en/java/javase/20/docs/api/)

예제에서 Command는 랜덤 숫자 발생이라는 사건이 일어났을 때 콘솔에 별을 찍는다. Invoker에서는 랜덤 숫자가 발생하면  Command 객체를 생성하고  생성한 Command객체를 Macro에 저장하고 실행한다. 명령 그 자체를 Macro에 객체로 저장함으로써 이전에 수행하여 누적된 명령들을 재이용하여 모두 일괄 실행할 수 있다. 조건에 따라 마지막 명령을 제거(undo)하거나 명령 이력을 클리어하고 있다.

▷Command 역할 - Command.java

package main.java.designpattern.command2;

public interface Command {
    public abstract void execute();
}

 

▷ConcreteCommand 역할 - ConcreteCommand.java

package main.java.designpattern.command2;

public class ConcreteCommand implements Command {
    private Receiver receiver;
    private int data;

    public ConcreteCommand(Receiver receiver, int data){
        this.receiver = receiver;
        this.data = data;
    }
    @Override
    public void execute() {
        receiver.action(data);
    }
}

 

▷Receiver 역할 - Receiver.java

package main.java.designpattern.command2;

public interface Receiver {
    public abstract void action(int data);
}

 

▷ConcreteReceiver 역할 - ConcreteReceiver.java

package main.java.designpattern.command2;

public class ConcreteReceiver implements Receiver{
    @Override
    public void action(int data) {
        for(int i=0; i<data; i++){
            System.out.print("* ");
        }
        System.out.println();
    }
}

 

▷Invoker 역할 - Invoker.java

package main.java.designpattern.command2;

import java.util.Random;

public class Invoker {
    public static void main(String[] args){
        Random random = new Random();
        Receiver receiver = new ConcreteReceiver();
        MacroCommand macroCommand = new MacroCommand();

        for(int i=0; i<20; i++){
            System.out.println();
            System.out.println("클라이언트의 요청 숫자 : " + i);
            int rand = random.nextInt(10) + 1;
            System.out.println("Command 객체를 생성합니다.");
            Command c = new ConcreteCommand(receiver, rand);
            macroCommand.add(c);
            macroCommand.execute();

            if(i%3 == 0){
                macroCommand.undo();
            }
            if(i==15){
                macroCommand.clear();
            }

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }



    }
}

 

예제 실행 모습.

---------------------------------------------------------
※참고자료
-Java언어로 배우는 디자인 패턴 입문
-Headfirst Design Pattern
-나무위키, 위키백과, 네이버백과
-리팩토링구루(https://refactoring.guru/ko)

반응형