[디자인 패턴] 커맨드 패턴(Command Pattern)

James·2023년 8월 2일
1
post-thumbnail

커맨드 패턴이란?


커맨드 패턴은 객체 지향 디자인 패턴 중 하나로, 객체 간의 결합도를 낮추고 유연성을 높이는 패턴입니다.

커맨드 패턴의 주요 목적

커맨드 패턴의 주요 목적은 요청을 객체의 형태로 캡슐화하여 매개변수화하고, 이를 통해 메서드를 호출하는 클래스와 메서드를 구현하는 클래스 사이의 결합을 느슨하게 만드는 것입니다. 이로써 클라이언트가 요청의 수신자를 알 필요 없이 다양한 요청을 보낼 수 있게 됩니다.

커맨드 패턴 & 의존성

Q) 커맨드 패턴에서 의존성완전 제거 되는가 ?

결론 : NO
커맨드 패턴은 객체 간의 결합도를 낮추는 데에 도움이 되지만, 의존성을 완전히 제거하는 것은 아닙니다. 대신, 더 느슨한 결합을 이룰 수 있도록 도와줍니다.

기존에 직접적으로 서로를 호출하던 클래스들 사이에서 커맨드 패턴을 적용하면, 커맨드 객체를 통해 호출하도록 변경됩니다. 이로 인해 호출자 클래스가 수신자 클래스에 대해 직접적인 의존성을 갖지 않게 되며, 커맨드 클래스가 호출자와 수신자를 연결해줍니다.

따라서, 호출자 클래스가 수신자 클래스의 세부 구현에 의존하지 않게 됩니다.

커맨드 패턴 사용이유?

구글홈이라고 "OK Google 히터 틀어줘" 라고 하면, 히터를 틀어주는 실제 구글 서비스가 있습니다.

구글홈을 사용하는 사용자를 Client 클래스

구글홈을 OKGoogle 클래스,

히터를 Heater 클래스로 정의한다.

OKGoogle의 기능이 많아질수록 객체 프로퍼티는 더욱 늘어날 것이고,

기존의 talk() 메서드에서 분기가 늘어날 것입니다.

OCP(개방 폐쇄 원칙) 에도 위배되죠.

때문에, 커맨드 패턴이 필요한 것이다.

커맨드 패턴으로 해결

먼저 OKGoogle이 할 수 있는 기능들(Heater를 튼다, Lamp를 킨다.) 을 클래스로 만들어서( HeaterOnCommand, LampOnCommand ) 각 기능들을 캡슐화 합니다.

그리고 OKGoogle 클래스의 talk() 메서드에서 heater.powerOn() , lamp.turnOn()과 같이 기능들을 직접 호출하지 않고,

캡슐화한 Command 인터페이스의 메서드를 호출하도록 합니다.

  1. 먼저 인터페이스를 정의합니다.
public interface Command {
    public void run();
}
  1. Heater를 켜는 명령을 클래스화 하여, HeaterOnCommand 클래스를 정의하고, Heater 클래스는 그대로 히터를 켜는 powerOn() 메서드를 정의합니다.
public class HeaterOnCommand implements Command{
    private Heater heater;

    public HeaterOnCommand(Heater heater){
        this.heater = heater;
    }

    public void run(){
        heater.powerOn();
    }
}
public class Heater {
    public void powerOn(){
        System.out.println("Heater on");
    }
}
  1. 마찬가지로 Lamp를 켜는 명령을 클래스화 하여, LampOnCommand 클래스를 정의하고, Lamp 클래스는 그대로 램프를 켜는 turnOn() 메서드를 정의합니다.
public class LampOnCommand implements Command{
    private Lamp lamp;

    public LampOnCommand(Lamp lamp){
        this.lamp = lamp;
    }

    public void run(){
        lamp.turnOn();
    }
}
public class Lamp {
    public void turnOn(){
        System.out.println("Lamp on");
    }
}
  1. OKGoogle 클래스의 talk() 메서드에서는 Command 인터페이스의 run() 메서드를 하여 명령을 실행합니다.
public class OKGoogle {
    private Command command;

    public void setCommand(Command command){
        this.command = command;
    }

    public void talk(){
        command.run();
    }
}
  1. 마지막으로 OKGoogle을 사용하는 Client 클래스를 정의합니다.
public class Client {
    public static void main(String args[]){
        Heater heater = new Heater();
        Lamp lamp = new Lamp();

        Command heaterOnCommand = new HeaterOnCommand(heater);
        Command lampOnCommand = new LampOnCommand(lamp);
        OKGoogle okGoogle = new OKGoogle();

        // 히터를 켠다
        okGoogle.setCommand(heaterOnCommand);
        okGoogle.talk();

        // 램프를 켠다
        okGoogle.setCommand(lampOnCommand);
        okGoogle.talk();

    }
}

만약 OKGoogle에 TV를 틀어줘 기능이 추가된다면,

TVOnCommand 클래스를 추가하면 되므로, OCP에 위배되지 않으면서 기능을 추가할 수 있습니다.

Reference & Additional Resources

profile
의미있는 성장의 태도, 긍정적인 사고를 지닌 Deveolper

0개의 댓글