디자인패턴_Command Pattern of Behavioral Pattern

2021. 1. 14. 16:102021/JOB DA STUDY

Command Pattern이란,

실행될 기능을 캡슐화해 주어진 여러 기능을 실행할 수 있는 재사용성 높은 클래스를 설계하는 패턴

 

  • 이벤트 발생시 실행될 기능이 다양하면서도 변경이 피요한 경우 이벤트를 발생시키는 클래스를 변경하지 않고 재사용하고자 할 때 유용
  • 실행될 기능을 캡슐화해 기능의 실행을 요구하는 호출자(Invoker)클래스실제 기능을 실행하는 수신자(Receiver)클래스 사이의 의존성을 제거 -> 수신자의 변경에도 호출자의 수정없이 그대로 사용 가능

  • Invoker

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

 

  • Command

실행될 기능에 대한 인터페이스 -> 실행될 기능에 대해 execute 메서드로 선언

 

  • ConcreteCommand

실제로 실행될 기능을 구현 -> Command 인터페이스를 구현

 

  • Receiver

ConcreteCommand에서 execute 메서드를 구현할 때 필요한 클래스 -> ConcreteCommand 기능을 실행하기 위해 사용하는 수신자 클래스

 

***Behavioral Pattern***

- 객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴

- 한 객체가 혼자 수행 할 수 없는 작업을 여러 개의 객체로 분배하는 방법 & 객체 사이의 결합도 최소화에 중점

 

만능 버튼 만들기

  • 버튼을 누르면 램프의 불이 켜지는 프로그램
public class Lamp{
    public void turnOn(){ Systme.out.println("Lamp On"); }
}

 public class Button {
     ptivate 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);
        //Button 클래스의 Button(Lamp theLamp) 생성자를 이용해 불을 켤 객체(lampButton)을 전달
        lampButton.pressed();
        //Button 클래스의 pressed()가 호출되면 theLamp 생성자를 통해 전달 받은 Lamp객체의 turnOn()을 호출          해 불을 켠다.
    }
}

 

문제점

* 버튼을 눌렀을 때 다른 기능을 실행하는 경우 (Ex-버튼을 눌리면 알람이 시작되는 경우)

public class (Lamp->)Alarm {
    public void start(){ Systme.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();
    }
}

 

- 새로운 기능으로 변경 (Lamp -> Alarm) 하려고, 기존 코드(Button 클래스)의 내용을 수정해야 하므로 OCP에 위배

 

* 버튼을 누르는 동작에 따라 다른 기능을 실행하는 경우 (Ex-버튼을 처음 눌렀을때는 램프를 두번째 눌렀을 때는 알람을 동작)

enum Mode { LAMP, ALARM };
public class Button {
    private Lamp theLamp;
    private Alarm theAlarm;
    private Mode theMode;

    //생성자에서 버튼을 눌렀을때 필요한 기능을 인자로 받는다.
    public Button(Lamp theLamp, Alarm theAlarm){
         this.theLamp = theLamp;
         this.theAlarm = theAlarm;
    }
    
    // Lamp 또는 Alarm 모드 설정
    public void setMdoe(Mode mode) {this.theMode = mode;}

    //설정된 모드에 따라 램프를 켜거나, 알람을 울림
    public void pressed(){
         swich(theMode){
            case LAMP: 
                 theLamp.turnOn();
                     break;
            case ALARM:
                theAlarm.start();
                    break;;
            }
     }

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

 

해결책

구체적 기능을 직접 구현하는 대신 실행될 기능을 캡슐화해야 한다.

 

Button 클래스의 pressed 메서드에서 구체적인 기능 (램프 켜기, 알람 동작 등)을 직접 구현하는 대신

버튼을 눌렀을 때 실행될 기능을 Button클래스 외부에서 제공받아 캡슐화해 Pressed메서드에서 호출한다.

-> Button클래스는 코드를 수정하지 않고 그대로 사용 가능

 

  • Button 클래스는 미리 약속된 Command 인터페이스의 execute 메서드르 호출
램프를 켜는 경우 theLamp.turnOn() 메서드 호출
알람이 동작하는 경우 theAlarm.start() 메서드를 호출

 

  • LampOnCommand OR AlarmOnCommand 클래서에서는 Command 인터페이스의 execute 메서드를 구현해 Lamp클래스의 turnOn() OR Alarm클래스의 start() 메서드 호출

Command 클래스

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

 

Button 클래스

public class Button{
    private Command theCommand;
    
     //생성자에서 버튼을 눌렀을 때 필요한 기능을 인자로 받는다.
     public void setCommand(Command newCommand) { this.theCommand = new Command; }
     public Button(Command theCommand) { setCommand(theCommand); }

     //버튼을 누르면 Command에 대한 execute 메서드를 호출한다.
     public void pressed() { theCommand.execute(); }

 

 

Lamp & LampOnCommand 클래스

public class Lamp{
    public void turnOn(){ Systme.out.println("Lamp On"); }
}

//램프를 켜는 LampOnCommand 클래스 (Command  상세 구현)
public class LampOnCommand implements Command {
    private Lamp theLamp;
    public LampOnCommand(Lamp theLamp) {this.theLamp = theLamp; }

    //Command 인터페이스의 execute 메서드
    public void execute() { theLamp.turnOn(); }
}

 

Alarm & AlarmStartCommand 클래스

public  class Alarm {
    public void start() { System.out.println("Alarming"); }
}
   
//알람을 울리는 AlarmStartCommand 클래스 (Command 상세 구현)
public class AlarmStartCommand implements Command{
    private Alarm theAlarm;
    public AlarmStartCommand(Alarm theAlarm) { this.theAlarm = theAlarm; }

    //Command 인터페이스의 execute 메서드
    public void execute() { theAlarm.start(); }
}

 

Client

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

        // 1. 램프 키기 Command 설정
        Button button1 = new Button(lampOnCommand);
        //램프 켜기 기능 수행
        button.pressed();

        // 2. 알람 울리는 Command 설정
        Button button2 = new Button(alarmStartCommand);
        button2.pressed();

        // 3. 알람 울리는 설정을 램프 켜기 설정으로 변경
        button2.setCommand(lampOnCommand);
        button3.pressed();

 

 

 

 

  1. Command 인터페이스를 구현하는 LampOnCommand 와 AlarmStartCommand 객체를 Button객체에 설정한다.
  2. Button 클래스의 Pressed 메서드에서 Command 인터페이스의 execute 메서드를 호출한다.

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'2021 > JOB DA STUDY' 카테고리의 다른 글

디자인패턴_Template Pattern of Behavioral Pattern  (0) 2021.01.15
Super  (0) 2021.01.15
Interface  (0) 2021.01.14
포트포워딩  (0) 2021.01.14
VMware Network  (0) 2021.01.12