커맨드 패턴
Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 87개 중 70번째.
커맨드 패턴(Command Pattern)은 요청을 요청에 대한 모든 정보를 포함하는 독립적인 객체로 변환하는 행동 디자인 패턴입니다. 이러한 변환을 통해 요청을 메서드 인자로 전달하거나, 요청의 실행을 지연 또는 대기열에 넣을 수 있으며, 취소 가능한 작업을 지원할 수 있습니다.
레스토랑을 예로 들어 보겠습니다. 요리사에게 무엇을 요리할지 직접 말하는 대신, 주문서에 주문 내용을 적습니다. 웨이터(invoker)는 이 주문서(command)를 주방으로 가져가며, 주방에서는 이를 대기열에 추가하거나, 우선순위를 정하거나, 심지어 취소할 수도 있습니다. 이 패턴은 작업을 호출하는 객체와 작업을 수행하는 객체를 분리합니다.
이 패턴은 네 가지 핵심 구성 요소로 이루어져 있습니다: execute 메서드를 선언하는 Command 인터페이스, 특정 작업을 구현하는 ConcreteCommand 클래스들, 실제 작업을 수행하는 Receiver, 그리고 명령을 트리거하는 Invoker입니다:
interface Command {
void execute();
}class Light { // 수신자
public void turnOn() {
System.out.println("Light is ON");
}
public void turnOff() {
System.out.println("Light is OFF");
}
}class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}class RemoteControl { // 호출자
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}인보커(invoker)는 어떤 작업이 수행될지 알지 못하며, 단지 execute()를 호출할 뿐입니다:
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // 출력: 전등이 켜졌습니다커맨드 패턴은 작업을 큐에 넣거나, 실행 취소/다시 실행 기능을 구현하거나, 나중에 실행할 작업을 예약해야 할 때 탁월합니다. 각각의 커맨드는 작업을 수행하는 데 필요한 모든 것을 캡슐화하여 시스템을 더욱 유연하고 확장 가능하게 만듭니다.
챌린지
쉬움커맨드 패턴(Command Pattern)을 사용하여 스마트 홈 자동화 시스템을 구축해 봅시다! 다양한 가전 기기를 커맨드를 통해 제어할 수 있는 시스템을 만들게 됩니다. 이를 통해 각 기기가 내부적으로 어떻게 작동하는지 알 필요 없이, 마치 유니버설 리모컨처럼 동작을 큐에 추가하거나 실행하고, 나중에 사용하기 위해 저장할 수도 있습니다.
코드는 다음 네 개의 파일로 구성됩니다:
Command.java: 모든 기기 커맨드가 구현할Command인터페이스를 정의합니다. 커맨드의 동작을 수행하는 단일 메서드execute()를 선언해야 합니다.Devices.java: 스마트 홈 기기를 나타내는 두 개의 수신자(receiver) 클래스를 생성합니다:Television-TV is now ON을 출력하는powerOn()메서드,TV is now OFF를 출력하는powerOff()메서드, 그리고TV channel set to [channel]을 출력하는setChannel(int channel)메서드를 가집니다.Thermostat-Thermostat set to [temp] degrees를 출력하는setTemperature(int temp)메서드와Thermostat turned OFF를 출력하는turnOff()메서드를 가집니다.Commands.java: 기기 조작을 캡슐화하는 구체적인 커맨드 클래스들을 생성합니다:TVOnCommand- 생성자에서Television을 인자로 받고, 실행 시powerOn()을 호출합니다.TVOffCommand- 생성자에서Television을 인자로 받고, 실행 시powerOff()을 호출합니다.TVChannelCommand- 생성자에서Television과int channel을 인자로 받고, 실행 시 해당 채널로setChannel()을 호출합니다.ThermostatSetCommand- 생성자에서Thermostat과int temperature를 인자로 받고, 실행 시 해당 값으로setTemperature()를 호출합니다.Main.java: 커맨드를 저장하는setCommand(Command cmd)메서드와 저장된 커맨드를 실행하는pressButton()메서드를 가진RemoteControl클래스(호출자, invoker)를 생성합니다.main 메서드에서는 TV 채널 번호(정수)와 서모스탯 온도(정수)라는 두 개의 입력을 받습니다.
Television과Thermostat객체를 생성합니다.RemoteControl을 생성하고 다음 순서대로 커맨드 패턴을 시연합니다:TVOnCommand를 설정하고 버튼을 누릅니다.- 입력받은 채널로
TVChannelCommand를 설정하고 버튼을 누릅니다. - 입력받은 온도로
ThermostatSetCommand를 설정하고 버튼을 누릅니다. TVOffCommand를 설정하고 버튼을 누릅니다.
TV 채널(정수)과 서모스탯 온도(정수) 순서대로 두 개의 입력을 받게 됩니다.
예를 들어, 입력값이 5와 72인 경우 출력은 다음과 같습니다:
TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFFRemoteControl이 기기와 직접 상호작용하지 않고 오직 Command 인터페이스만 알고 있다는 점에 주목하세요. 각 커맨드는 동작을 수행하는 데 필요한 모든 것을 캡슐화하므로, 호출자를 변경하지 않고도 새로운 기기와 커맨드를 쉽게 추가할 수 있습니다!
치트 시트
커맨드 패턴(Command Pattern)은 요청을 객체로 캡슐화하여, 서로 다른 요청을 가진 클라이언트를 매개변수화하고, 요청을 큐에 저장하거나 로그로 기록하며, 취소 가능한 작업을 지원할 수 있게 하는 행동 디자인 패턴입니다.
이 패턴은 작업을 호출하는 객체(invoker)와 작업을 수행하는 객체(receiver)를 분리합니다.
주요 구성 요소
- Command:
execute()메서드를 선언하는 인터페이스 - ConcreteCommand: 특정 작업을 구현하는 클래스들
- Receiver: 실제 작업을 수행하는 객체
- Invoker: 커맨드를 트리거하는 객체
기본 구조
// Command 인터페이스
interface Command {
void execute();
}
// Receiver
class Light {
public void turnOn() {
System.out.println("Light is ON");
}
public void turnOff() {
System.out.println("Light is OFF");
}
}
// ConcreteCommand
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}
// Invoker
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}사용법
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // 출력: Light is ON호출자(invoker)는 어떤 작업이 수행될지 알지 못하며, 단지 execute()를 호출할 뿐입니다. 이는 시스템을 유연하고 확장 가능하게 만들며, 작업 큐잉, 실행 취소/다시 실행 기능 구현 또는 작업 스케줄링에 이상적입니다.
직접 해보기
import java.util.Scanner;
// TODO: 다음을 포함하는 RemoteControl 클래스(invoker)를 생성하세요:
// - 명령을 저장하는 setCommand(Command cmd) 메서드
// - 저장된 명령을 실행하는 pressButton() 메서드
class RemoteControl {
// TODO: RemoteControl 구현하기
}
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 입력 읽기
int channel = scanner.nextInt();
int temperature = scanner.nextInt();
// TODO: Television과 Thermostat 생성하기
// TODO: RemoteControl 생성하기
// TODO: Command 패턴 시연하기:
// 1. TVOnCommand를 설정하고 버튼을 누릅니다
// 2. 입력받은 채널로 TVChannelCommand를 설정하고 버튼을 누릅니다
// 3. 입력받은 온도로 ThermostatSetCommand를 설정하고 버튼을 누릅니다
// 4. TVOffCommand를 설정하고 버튼을 누릅니다
scanner.close();
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
4상속
상속의 기초 (extends)super 키워드메서드 오버라이딩 (@Override)생성자 체이닝Object 클래스단일 및 다중 레벨 상속다중 클래스 상속이 불가능한 이유요약 - 직원 계층 구조