커맨드 패턴

차동준·2022년 7월 20일
0

CS-디자인패턴

목록 보기
8/16
post-thumbnail

👨‍💻 커맨드 패턴이란?


어떤 것을 요구하는 객체(Invoker)
요구를 받아들이고 처리하는 객체(Receiver)를 분리하고,
실행될 수 있는 기능들을 캡슐화하여 둘 사이의 의존성을 제거하는 디자인 패턴

예시를 들어서 설명하자면, 리모컨의 버튼이 항상 같은 기능만을 하게끔 미리 설정하는 것이 아니라
사용자가 원하는 대로 커스텀하여 사용할 수 있게 하는 것이다.

어떤 리모컨(Invoker: 요구하는 객체)이 버튼을 눌렀을 때(요구했을 때)
조명(Receiver: 처리하는 객체)이 꺼지거나 켜지는 등의 행동을 하게 하고,

혹은 어떤 버튼을 눌렀을 때는
TV가 Receiver가 되어 TV가 꺼지거나 켜지는 등의 행동을 하게 하는 것이다.

이렇게 분리하게 되면, 취소기능도 버튼에 하나하나 구현할 필요 없이
리시버에 구현하기만 하고 설정만 해준다면, 기존 코드의 변경 없이 확장이 가능하다(OCP)

추가로, 로그를 기록하는 데에도 매우 수월하다.
리시버 별로 따로 로그를 기록하면 되기 때문에

Invoker에서는 그냥 실행만 하더라도 리시버에서 로그를 처리하게 된다.
만약 리시버를 분리하지 않았다면?
Invoker안에 버튼의 기능을 바꿔줄 때마다 로그도 바꿔줘야하고 기능도 바꿔줘야 한다.
이말은 즉슨, SOLID의 "O(OCP)"를 완벽하게 위배할 수 있는 행위이다.


예시) 실행될 수 있는 기능을 캡슐화한(인터페이스화한) Command 인터페이스

위의 리모컨 예시를 잠시 갖고 와서 다시 설명하면,

public interface Command {
	public void execute();
}

실행될 수 있는 기능(커맨드들)을 추상화한 Command 인터페이스가 있다.
이 인터페이스에는 execute()라는 실행 함수만이 추상메소드로 선언되어있다.


예시) Receiver 클래스(조명에 관한 기능을 갖고 있는 리시버 클래스)

public class Light {
	public void on() { System.out.pritnln("조명 켜짐"); }
    public void off() { System.out.pritnln("조명 꺼짐"); }
}

그리고, 명령을 수행을 처리할 리시버 객체인 Light 클래스와,
이 리시버 객체를 멤버로 갖고 있는 커맨드 객체인 LightCommand가 있다.


예시) Command 클래스(조명 Receiver 정보를 갖고 있는 조명 커맨드 클래스)

public LightCommand implements Command {
	Light light;
    
    public LightCommand(Light light) {
    	this.light = light;
    }
    
    public void execute() {
    	light.on();
    }
}

커맨드 객체인 LightCommand는 리시버 객체에 대한 정보와 Command 인터페이스의
실행(execute)을 재정의한 함수 정보가 들어있다.


예시) Invoker 클래스(버튼을 누를때 작업을 요청하는 리모콘 인보커 클래스)

public RemoteController {
	Command button;
    public RemoteController() {}
    
    public void setButton(Command command) {
    	this.button = command;
    }
    
    public void buttonPressed() {
   		button.execute();
    }
}

따라서, 리모컨(Invoker)객체의 버튼(커맨드 타입 멤버)에
커맨드객체를 설정해주면 어떤 커맨드 객체가 필요하든
Invoker에서는 setter를 통해서 설정만 해주면 되는 것이다.

이처럼 Invoker(리모컨)와 Command(조명명령)-Receiver(조명에관한명령수행)를 분리해서
의존성을 떨어뜨리는 것이 커맨드 패턴의 주된 목적이다



예시) 테스트

public static void main(String[] args) {
	RemoteController rc = new RemoteController();
    LightCommand lightCommand = new LightCommand(new Light()); // 조명 커맨드 객체 생성
    
    rc.setbutton(lightCommand);
    rc.execute(); // 조명 켜짐
    
    rc.setButton(tvCommand);
    rc.execute(); // TV 켜짐
}



🔎 커맨드 패턴의 장단점


장점

  1. 시스템의 결합도(의존성)을 줄여주기 때문에 변경엔 닫혀있고 확장엔 열려있다.(OCP)
  2. 기능추가를 위해서 커맨더와 리시버 클래스를 새롭게 만들면 되기 때문에 기능 확장이 유연하다.
  3. 각각의 커맨드와 리시버가 SRP(단일 책임 원칙)에 맞는 하나의 책임만 갖는다.(SRP)

단점

  1. 복잡도가 증가한다.
  2. 어떠한 기능이 추가될 때마다 그에 맞는 커맨드, 리시버 클래스를 생성해주어야 하기 때문에 번거롭다.
profile
백엔드를 사랑하는 초보 개발자

0개의 댓글