Command Pattern

김영우 (AvocadoSmasher)·2022년 12월 5일
0

Design Pattern

목록 보기
7/8

🔎 What is Command Pattern??

Command Pattern은 어떤 객체가 수행할 기능을 Command라는 하나의 Interface로 묶어(캡슐화 시킴) 해당 객체가 할 수 있는 행위를 동적으로 지정할 수 있도록 하여 코드의 재사용성을 높이는 디자인 패턴입니다.

Command Pattern에서는 일반적으로 Invoker, Command, ConcreteCommand, Receiver 이렇게 4가지 파트로 구성이 되는데 각 파트는 다음과 같습니다.

  • Invoker 실제로 기능을 호출하여 사용하는 클래스.
  • Command Command Pattern에서 기능 부분에 대한 Interface. Command Pattern으로 캡슐화를 할때 실제 Invoker의 최종 Entry Point.
  • ConcreteCommand Command Pattern에서 Command내에서도 여러 행위, 기능들이 존재할 수 있는데 그때 각각의 기능들로 이어지는 통로 같은 역할. Command Interface를 implement하며 Receiver를 내부에 속성으로 가져 해당 Receiver의 행위를 Command Interface에서 표준화한 메소드로 연결해준다.
  • Receiver 실제 기능을 구현해놓은 클래스.

이를 간단한 클래스 다이어그램으로 표현한다면 다음과 같습니다.

Untitled

⚓️ Digging into how it works

왜 이렇게까지 표현을 하는가?를 한번 짚어보도록 하겠습니다. 예를 들어서 여러분은 지금 Switch 디자인 중입니다. Switch라고 한다면 어떤게 떠오르나요? 저는 제 방에 있는 제 방 Switch가 떠오르네요.

저희 집에는 많은 Switch가 있고 이들은 각각 각 방, 위치별 불을 끄고 키는 기능을 합니다. 같은 스위치, 같은 스위치를 키고 끈다는 기능이지만 실제로 일어나는 일, 즉 기능은 조금씩 다름을 볼 수 있죠?

그렇다면 각 스위치별 기능을 그냥 단순하게 switch문으로 구분하여 하나의 클래스에 다 넣는다면 어떨가요? 이는 OOP에서 지향하는 기능과 책임의 분리에 반하는 방법임으로 수정, 확장에 좋지 못합니다.

그럼 각 방별 불이 켜지고 꺼지는 책임을 분리시켜 준다면 어떨까요? 이러한 접근이 결국 Command Pattern의 필요성으로 수렴하게 되는 것입니다. 자세한 쿄드는 아래서 살펴보도록 하죠.

💻 Code (feat. Java)

간단하게 원룸을 생각해보죠. 원룸에는 거실 불, 부엌의 후드를 키는 Button이 있고 이를 Command Pattern을 적용하여 디자인 해봅시다. 디자인된 실제 코드는 다음과 같습니다.

// Invoker
public class Button {
    Command command;
    public Button(Command command){
        this.command = command;
    }
    public void clicked(){
        command.execute();
    }
}
// Command Interface
public interface Command {
    public void execute();
}
// ConcreteCommand
public class HoodOnCommand implements Command{

    Hood hood;
    public HoodOnCommand(Hood hood){
        this.hood = hood;
    }
    @Override
    public void execute() {
        hood.start();
    }
}
// ConcreteCommand
public class LivingRoomLightOnCommand implements Command{
    LivingRoomLight livingRoomLight;
    public LivingRoomLightOnCommand(LivingRoomLight livingRoomLight){
        this.livingRoomLight = livingRoomLight;
    }
    @Override
    public void execute() {
        livingRoomLight.on();
    }
}
// Receiver
public class Hood {
    public void start(){
        System.out.println("---Hood is working---");
    }
}
// Receiver
public class LivingRoomLight {
    public void on(){
        System.out.println("LivingRoomLight has been turned on");
    }
}
public class Main {
    public static void main(String[] args){
        Hood hood = new Hood();
        HoodOnCommand hoodOnCommand = new HoodOnCommand(hood);
        LivingRoomLight livingRoomLight= new LivingRoomLight();
        LivingRoomLightOnCommand livingRoomLightOnCommand = new LivingRoomLightOnCommand(livingRoomLight);

        Button s1 = new Button(hoodOnCommand);
        Button s2 = new Button(livingRoomLightOnCommand);
        s1.clicked();
        s2.clicked();
    }
}

📌 Summary

오늘은 Command Pattern을 통해서 어떻게 기능에 대한 책임을 어떻게 분리하여 코드의 재사용성을 높이는지 알아보았습니다. 예제 코드는 사실 너무 많아지면 보기도 불편하고 작성도 여러보로 귀찮아서 생략한 부분도 있습니다.(실제로는 On, Off에 대해 각각 ConcreteCommand를 지정하고 Invoker에서 setCommand와 같은 메소드를 정의하여 on, off에 대해서 받는 형태.) 뭐 그래도 해당 코드들로만으로도 대강 어떤 형태로 이루어 지는지는 알 수 있다고 생각하고 각각의 예시는 각자 해보는게 좋지 않을까요? 그럼 이만.

profile
Android Studio 공부 중

0개의 댓글