커맨드 디자인 패턴
명령 패턴은 행동 디자인 패턴 중 하나입니다. 명령 디자인 패턴은 요청-응답 모델에서 느슨한 결합을 구현하는 데 사용됩니다.
명령 패턴

명령 디자인 패턴 예제
명령 패턴을 구현할 수 있는 실제 시나리오를 살펴보겠습니다. 파일을 열고, 쓰고, 닫는 메서드가 있는 파일 시스템 유틸리티를 제공한다고 가정해 보겠습니다. 이 파일 시스템 유틸리티는 Windows 및 Unix와 같은 여러 운영 체제를 지원해야 합니다. 파일 시스템 유틸리티를 구현하려면 먼저 실제로 모든 작업을 수행할 수신기 클래스를 만들어야 합니다. 우리는 자바에서 인터페이스 측면에서 코딩하기 때문에 FileSystemReceiver
인터페이스를 가질 수 있고 Windows, Unix, Solaris 등과 같은 다양한 운영 체제에 대한 구현 클래스를 가질 수 있습니다.
명령 패턴 수신기 클래스
package com.journaldev.design.command;
public interface FileSystemReceiver {
void openFile();
void writeFile();
void closeFile();
}
FileSystemReceiver 인터페이스는 구현 클래스에 대한 계약을 정의합니다. 간단히 하기 위해 Unix 및 Windows 시스템에서 작동하는 두 가지 종류의 수신기 클래스를 만들고 있습니다.
package com.journaldev.design.command;
public class UnixFileSystemReceiver implements FileSystemReceiver {
@Override
public void openFile() {
System.out.println("Opening file in unix OS");
}
@Override
public void writeFile() {
System.out.println("Writing file in unix OS");
}
@Override
public void closeFile() {
System.out.println("Closing file in unix OS");
}
}
package com.journaldev.design.command;
public class WindowsFileSystemReceiver implements FileSystemReceiver {
@Override
public void openFile() {
System.out.println("Opening file in Windows OS");
}
@Override
public void writeFile() {
System.out.println("Writing file in Windows OS");
}
@Override
public void closeFile() {
System.out.println("Closing file in Windows OS");
}
}
재정의 주석을 확인하셨습니까? 주석이 사용되는 이유가 궁금하다면 재정의 주석 이점을 읽어보세요. 이제 수신자 클래스가 준비되었으므로 Command 클래스를 구현하도록 이동할 수 있습니다.
명령 패턴 인터페이스 및 구현
인터페이스 또는 추상 클래스를 사용하여 기본 Command를 만들 수 있습니다. 이는 디자인 결정이며 요구 사항에 따라 다릅니다. 기본 구현이 없기 때문에 인터페이스를 사용할 것입니다.
package com.journaldev.design.command;
public interface Command {
void execute();
}
이제 수신자가 수행하는 모든 다양한 유형의 작업에 대한 구현을 만들어야 합니다. 세 가지 조치가 있으므로 세 가지 명령 구현을 작성합니다. 각 명령 구현은 요청을 적절한 수신자 방법으로 전달합니다.
package com.journaldev.design.command;
public class OpenFileCommand implements Command {
private FileSystemReceiver fileSystem;
public OpenFileCommand(FileSystemReceiver fs){
this.fileSystem=fs;
}
@Override
public void execute() {
//open command is forwarding request to openFile method
this.fileSystem.openFile();
}
}
package com.journaldev.design.command;
public class CloseFileCommand implements Command {
private FileSystemReceiver fileSystem;
public CloseFileCommand(FileSystemReceiver fs){
this.fileSystem=fs;
}
@Override
public void execute() {
this.fileSystem.closeFile();
}
}
package com.journaldev.design.command;
public class WriteFileCommand implements Command {
private FileSystemReceiver fileSystem;
public WriteFileCommand(FileSystemReceiver fs){
this.fileSystem=fs;
}
@Override
public void execute() {
this.fileSystem.writeFile();
}
}
이제 수신자 및 명령 구현이 준비되었으므로 호출자 클래스 구현으로 이동할 수 있습니다.
명령 패턴 호출자 클래스
Invoker는 명령을 캡슐화하고 명령 개체에 요청을 전달하여 처리하는 간단한 클래스입니다.
package com.journaldev.design.command;
public class FileInvoker {
public Command command;
public FileInvoker(Command c){
this.command=c;
}
public void execute(){
this.command.execute();
}
}
파일 시스템 유틸리티 구현이 준비되었으며 간단한 명령 패턴 클라이언트 프로그램을 작성할 수 있습니다. 하지만 그 전에 적절한 FileSystemReceiver
객체를 생성하는 유틸리티 메서드를 제공하겠습니다. 팩토리 패턴을 사용하여 입력에 따라 적절한 유형을 반환할 수 있기 때문입니다.
package com.journaldev.design.command;
public class FileSystemReceiverUtil {
public static FileSystemReceiver getUnderlyingFileSystem(){
String osName = System.getProperty("os.name");
System.out.println("Underlying OS is:"+osName);
if(osName.contains("Windows")){
return new WindowsFileSystemReceiver();
}else{
return new UnixFileSystemReceiver();
}
}
}
이제 파일 시스템 유틸리티를 사용할 명령 패턴 예제 클라이언트 프로그램을 생성해 보겠습니다.
package com.journaldev.design.command;
public class FileSystemClient {
public static void main(String[] args) {
//Creating the receiver object
FileSystemReceiver fs = FileSystemReceiverUtil.getUnderlyingFileSystem();
//creating command and associating with receiver
OpenFileCommand openFileCommand = new OpenFileCommand(fs);
//Creating invoker and associating with Command
FileInvoker file = new FileInvoker(openFileCommand);
//perform action on invoker object
file.execute();
WriteFileCommand writeFileCommand = new WriteFileCommand(fs);
file = new FileInvoker(writeFileCommand);
file.execute();
CloseFileCommand closeFileCommand = new CloseFileCommand(fs);
file = new FileInvoker(closeFileCommand);
file.execute();
}
}
클라이언트는 적절한 유형의 명령 개체를 생성할 책임이 있습니다. 예를 들어 파일을 작성하려는 경우 CloseFileCommand
객체를 생성해서는 안 됩니다. 클라이언트 프로그램은 또한 수신기를 명령에 연결한 다음 명령을 호출자 클래스에 연결해야 합니다. 위 명령 패턴 예제 프로그램의 출력은 다음과 같습니다.
Underlying OS is:Mac OS X
Opening file in unix OS
Writing file in unix OS
Closing file in unix OS
명령 패턴 클래스 다이어그램

명령 패턴 중요 사항
- Command는 구현 계약을 정의하는 명령 설계 패턴의 핵심입니다.
- 수신자 구현은 명령 구현과 별개입니다.
- 명령 구현 클래스는 수신기 개체에서 호출할 메서드를 선택했으며 수신기의 모든 메서드에 대해 명령 구현이 있습니다. 수신자와 작업 방법 사이의 다리 역할을 합니다.
- Invoker 클래스는 클라이언트의 요청을 명령 개체로 전달합니다.
- 클라이언트는 적절한 명령 및 수신자 구현을 인스턴스화한 다음 함께 연결할 책임이 있습니다.
- 클라이언트는 또한 호출자 개체를 인스턴스화하고 명령 개체를 연결하고 작업 메서드를 실행합니다.
- 커맨드 디자인 패턴은 쉽게 확장할 수 있으며 클라이언트 코드를 변경하지 않고도 수신자에 새로운 작업 메서드를 추가하고 새로운 명령 구현을 생성할 수 있습니다.
- 커맨드 디자인 패턴의 단점은 코드가 거대해지고 액션 메서드 수가 많고 연결이 너무 많아 혼란스러워진다는 것입니다.
커맨드 디자인 패턴 JDK 예제
Runnable 인터페이스(java.lang.Runnable) 및 Swing Action(javax.swing.Action)은 명령 패턴을 사용합니다.