FIF's 코딩팩토리

커멘드 패턴(Command Pattern) 정리 본문

Back-End/Design Pattern(디자인 패턴)

커멘드 패턴(Command Pattern) 정리

FIF 2019. 6. 4. 09:31
반응형

1.커맨드 패턴이란

 A. 실행될 기능을 캡슐화 함으로써, 주어진 여러 기능을 수행할 수 있는 재사용성이 높은 클래스를 설계하는 패턴

 

 B. 이벤트가 발생했을 때, 실행될 기능이 다양하면서도 변경이 필요한 경우에 이벤트를 발생시키는 클래스를 변경하지 않고 재사용하고자 할 때 유용하다.

 

 

 C. ‘행위(Behavioral) 패턴’ 의 하나

 

 

D. 실행될 기능을 캡슐화 함으로써, 기능의 실행을 요구하는 호출자(Invoker)클래스와 실제 기능을 실행하는 수신자(Receiver)클래스 사이의 의존성을 제거한다.

 

E. 실행될 기능의 변경에도 호출자 클래스를 수정 없이 그대로 사용할 수 있도록 해준다.

 

F. 역할이 수행하는 작업

                       i. Command

                            1) 실행될 기능에 대한 인터페이스

                            2) 실행될 기능을 execute 메소드로 선언함

                       ii. ConcreteCommand

                            1) 실제로 실행되는 기능을 구현

                            2) 즉, Command라는 인터페이스를 구현함

                       iii. Invoker

 

1. 기능의 실행을 요청하는 호출자 클래스

                       iv. Receiver

                           1) ConcreteCommand에서 execute 메소드를 구현할 때 필요한 클래스

                           2) ConcreteCommand의 기능을 실행하기 위해 사용하는 수신자 클래스

 

2.   버튼 만들기

A.    

 

B. 버튼이 눌리면 램프의 불이 켜지는 프로그램

public class Lamp {
            public void turnOn() {
                  System.out.println("Lamp On");
            }
      }
      public class Button {
            private Lamp theLamp;          
            public Button(Lamp theLamp) {
                  this.theLamp = theLamp;
            }
            public void pressed() {
                  theLamp.turnOn();
            }

 

    public class Client {
            public static void main(String[] args) {
                  Lamp lamp = new Lamp();
                  Button lampButton = new Button(lamp);
                  lampButton.pressed();
            }

 

3.   문제점

A. 버튼을 눌렀을 때 다른기능을 실행하는 경우

                       i. 버튼을 눌렀을때, 알람을 시작하게 하려면?

public class Alarm {
               public void start() {
                      System.out.println("Alarming");
               }
       }
public class Button {
       private Alarm theAlarm;
       public Button(Alarm theAlarm) { this.theAlarm = theAlarm; }
                 public void pressed() { theAlarm.start(); }

 

   public class Client {
            public static void main(String[] args) {
                  Alarm alarm = new Alarm();
                  Button alarmButton = new Button(alarm);
                  alarmButton.pressed();
            }
      }

 

                      ii. 새로운 기능으로 변경하려고 기존 코드(Button클래스)의 내용을 수정해야 하므로 OCP에 위배된다.

                      iii. Button 클래스의 pressed()전체를 변경해야 한다.

 

B. 버튼을 누르는 동작에 따라 다른 기능을 실행하는 경우

                       i. 버튼을 처음 눌렀을 때는 램프를 켜고, 두 번째 눌렀을 때는 알람을 동작하게 하려면?

enum Mode {
             LAMP, ALARM
       };
       // Button 클래스의 코드를 수정
       public class Button {
             private Lamp theLamp;
             private Alarm theAlarm;
             private Mode theMode;
             // 생성자에서 버튼을 눌렀을 때 필요한 기능을 인지로 받는다.
             public Button(Lamp theLamp, Alarm theAlarm) {
                    this.theLamp = theLamp;
                    this.theAlarm = theAlarm;
             }
             // 램프 모드 또는 알람 모드를 설정
             public void setMode(Mode mode) {
                    this.theMode = mode;
             }
             // 설정된 모드에 따라 램프를 켜거나 알람을 울림
             public void pressed() {
                    switch (theMode) {
                    case LAMP:
                           theLamp.turnOn();
                           break;
                    case ALARM:
                           theAlarm.start();
                          break;
                    }
            }
      }

                      ii. 필요한 기능을 새로 추가할 때마다 Button 클래스의 코드를 수정해야 하므로 재사용하기가 어렵다.

4. 해결책

문제를 해결하기 위해서는 구체적인 기능을 직접 구현하는 대신 실행될 기능을 캡슐화 한다.

 

A. Button 클래스의 pressed 메소드에서 구체적인 기능(램프 켜기, 알람 동작 등)을 직접 구현하는 대신, 버튼을 눌렀을 때 실행될 기능을 Button 클래스 외부에서 제공받아 캡슐화해 pressed메소드에서 호출한다.

 

B. 이를 통해 Button 클래스 코드를 수정하지 않고 그대로 사용할 수 있다.

 

C. Button 클래스는 미리 약속된 Command 인터페이스의 execute 메소드를 호출한다.

                       i. 램프를 켜는 경우에는 theLamp.turnOn 메소드를 호출

                       ii. 알람이 동작하는 경우에는 theAlarm.start 메소드를 호출하도록 pressed 메소드를

                           수정한다.

D. LampOnCommand 클래스에서는 Command 인터페이스의 execute 메소드를 구현해서 Lamp 클래스의 turnOn 메소드(램프 켜는 기능)를 호출한다.

 

E. 마찬가지로 AlarmStartCommand  클래스는 Command 인터페이스의 execute 메소드를 구현해 Alarm 클래스의 start메소드(알람이 울리는 기능)를 호출한다.

 

F. Command 인터페이스

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

 

G. Button 클래스

 

public class Button{
   private Command theCommand;
//생성자에서 버튼을 눌렀을 때 필요한 기능을 인자로 받는다.
public Button(Command theCommand) {setCommand(theCommand); }
public void setCommand(Command newCommnad) {this.theCommand = newCommand; }
 
//버튼이 눌리면 주어진 Command의 execute 메소드를 호출한다.
public void pressed() { theCommand.execute(); }

H. Lamp, LampOnCommand 클래스

public class Lamp{
public void turnOn() {System.out.println(“Lamp On); }
}
//램프를 켜는 LampOnCommand 클래스
public class LampOnCommand implements Command{
   private Lamp theLamp;
   public LampOnCommand(Lamp theLamp) { this.theLamp = theLamp; }
//Command 인터페이스의 execute 메소드
public void execute() {theLamp.turn0n(); }
}

 

I. Alarm, AlarmStartCommand 클래스

public class Alarm {
      public void start() {
            System.out.println("Alarming");
      }
}
public class AlarmStartCommnad implements Command {
      private Alarm theAlarm;
      public AlarmStartCommand(Alarm theAlarm) {
            this.theAlarm = theAlarm;
      }
      public void execute() {
            theAlarm.start();
      }
}

 

J. 클라이언트에서의 사용

public class Client {

public static void main(String[] args) {

  Lamp lamp = new Lamp();
  Command lampOnCommand = new LampOnCommand(lamp);
  Alarm alarm = new Alarm();
  Command alarmStartCommand = new AlarmStartCommand(alarm);
  Button button1 = new Button(lampOnCommand); // 램프 켜는 Command 설정
  button1.pressed(); // 램프 켜는 기능 수행
  Button button2 = new Button(alarmStartCommand); // 알람 울리는 Command 설정
  button2.pressed(); // 알람 울리는 기능 수행
  button2.setCommand(lampOnCommand); // 다시 램프 켜는 Command로 설정
  button2.pressed(); // 램프 켜는 기능 수행
  }
}

 

K. Command 인터페이스를 구현하는 LampOnCommand와 AlarmStartCommand 객체를 Button 객체에 설정한다.

 

L. Button  클래스의 pressed메소드에서 Command 인터페이스의 execute 메소드를 호출한다.

 

M. 즉, 버튼을 눌렀을 때 필요한 임의의 기능은 Command 인터페이스를 구현한 클래스의 객체를 Button 객체에 설정해서 실행할 수 있다.

 

N. 이렇게 Command패턴을 이용하면 Button 클래스의 코드를 변경하지 않으면서 다양한 동작을 구현할 수 있게 된다.

 

반응형
Comments