1. 개요
명령 패턴은 동작 디자인 패턴이며 GoF 의 공식 디자인 패턴 List의 일부입니다. 간단히 말해서 패턴은 호출할 메서드, 메서드의 인수 및 메서드가 속한 개체를 포함하여 지정된 작업(명령)을 수행하는 데 필요한 모든 데이터를 개체에 캡슐화 하려고 합니다.
이 모델을 사용하면 소비자로부터 명령을 생성하는 개체를 분리 할 수 있으므로 패턴이 일반적으로 생산자-소비자 패턴으로 알려져 있습니다.
이 사용방법(예제)에서는 개체 지향 및 개체 기능 접근 방식을 모두 사용하여 Java에서 명령 패턴을 구현하는 방법을 배우고 어떤 사용 사례에서 유용할 수 있는지 살펴보겠습니다.
2. 객체 지향 구현
클래식 구현에서 명령 패턴은 명령, 수신자, 호출자 및 클라이언트의 네 가지 구성 요소 를 구현해야 합니다 .
패턴이 작동하는 방식과 각 구성 요소가 수행하는 역할을 이해하기 위해 기본 예제를 만들어 보겠습니다.
텍스트 파일 애플리케이션을 개발한다고 가정해 봅시다. 이 경우 텍스트 파일 열기, 쓰기, 저장 등과 같은 일부 텍스트 파일 관련 작업을 수행하는 데 필요한 모든 기능을 구현해야 합니다.
따라서 애플리케이션을 위에서 언급한 네 가지 구성 요소로 분류해야 합니다.
2.1. 명령 클래스
명령은 호출할 메서드, 메서드 인수 및 메서드를 구현하는 개체(수신자라고 함)를 포함하여 작업을 실행하는 데 필요한 모든 정보를 저장 하는 역할을 하는 개체입니다.
명령 개체가 작동하는 방식을 보다 정확하게 이해하기 위해 단일 인터페이스와 두 가지 구현을 포함하는 간단한 명령 계층 개발을 시작하겠습니다.
@FunctionalInterface
public interface TextFileOperation {
String execute();
}
public class OpenTextFileOperation implements TextFileOperation {
private TextFile textFile;
// constructors
@Override
public String execute() {
return textFile.open();
}
}
public class SaveTextFileOperation implements TextFileOperation {
// same field and constructor as above
@Override
public String execute() {
return textFile.save();
}
}
이 경우 TextFileOperation 인터페이스는 명령 개체의 API를 정의하고 OpenTextFileOperation 및 SaveTextFileOperation 의 두 가지 구현 이 구체적인 작업을 수행합니다. 전자는 텍스트 파일을 여는 반면 후자는 텍스트 파일을 저장합니다.
명령 개체의 기능을 보는 것은 분명합니다. TextFileOperation 명령 은 수신자 개체, 호출할 메서드 및 인수를 포함하여 텍스트 파일을 열고 저장하는 데 필요한 모든 정보를 캡슐화합니다 . 그러나 그들은 될 수 있습니다).
파일 작업을 수행하는 구성 요소가 수신기( TextFile 인스턴스) 라는 점을 강조할 가치가 있습니다.
2.2. 리시버 클래스
리시버는 일련의 응집력 있는 작업을 수행 하는 개체입니다 . 명령의 execute() 메서드가 호출될 때 실제 작업을 수행하는 구성 요소입니다.
이 경우 TextFile 개체 를 모델링하는 역할을 하는 수신자 클래스를 정의해야 합니다.
public class TextFile {
private String name;
// constructor
public String open() {
return "Opening file " + name;
}
public String save() {
return "Saving file " + name;
}
// additional text file methods (editing, writing, copying, pasting)
}
2.3. 호출자 클래스
호출자는 주어진 명령을 실행하는 방법을 알고 있지만 명령이 어떻게 구현되었는지는 모르는 객체입니다. 명령의 인터페이스만 알고 있습니다.
경우에 따라 호출자는 명령을 실행하는 것 외에도 명령을 저장하고 Queue에 넣습니다. 이는 매크로 기록 또는 실행 취소 및 다시 실행 기능과 같은 일부 추가 기능을 구현하는 데 유용합니다.
이 예제에서는 명령 개체를 호출하고 명령의 execute() 메서드 를 통해 실행하는 추가 구성 요소가 있어야 한다는 것이 분명해졌습니다 . 이것이 바로 호출자 클래스가 작동하는 곳 입니다.
호출자의 기본 구현을 살펴보겠습니다.
public class TextFileOperationExecutor {
private final List<TextFileOperation> textFileOperations
= new ArrayList<>();
public String executeOperation(TextFileOperation textFileOperation) {
textFileOperations.add(textFileOperation);
return textFileOperation.execute();
}
}
TextFileOperationExecutor 클래스는 소비자로부터 명령 개체를 분리하고 TextFileOperation 명령 개체 내에 캡슐화된 메서드를 호출하는 얇은 추상화 계층 입니다 .
이 경우 클래스는 명령 개체도 List 에 저장합니다 . 물론 작업 실행 프로세스에 추가 제어를 추가해야 하는 경우가 아니면 패턴 구현에서 필수 사항은 아닙니다.
2.4. 클라이언트 클래스
클라이언트는 실행할 명령과 실행할 프로세스의 단계를 지정 하여 명령 실행 프로세스를 제어 하는 개체입니다 .
따라서 패턴의 형식적 정의를 정통으로 사용하려면 일반적인 main 메서드 를 사용하여 클라이언트 클래스를 만들어야 합니다 .
public static void main(String[] args) {
TextFileOperationExecutor textFileOperationExecutor
= new TextFileOperationExecutor();
textFileOperationExecutor.executeOperation(
new OpenTextFileOperation(new TextFile("file1.txt"))));
textFileOperationExecutor.executeOperation(
new SaveTextFileOperation(new TextFile("file2.txt"))));
}
3. 객체 기능 구현
지금까지 명령 패턴을 구현하기 위해 개체 지향 접근 방식을 사용했는데 모두 훌륭했습니다.
Java 8부터는 람다 식 및 메서드 참조를 기반으로 하는 개체 기능 접근 방식을 사용하여 코드를 좀 더 간결하고 덜 장황하게 만들 수 있습니다.
3.1. 람다 식 사용
TextFileOperation 인터페이스는 기능적 인터페이스 이므로 TextFileOperation 인스턴스를 명시적으로 만들지 않고도 람다 식 형식의 명령 객체를 호출자에게 전달할 수 있습니다 .
TextFileOperationExecutor textFileOperationExecutor
= new TextFileOperationExecutor();
textFileOperationExecutor.executeOperation(() -> "Opening file file1.txt");
textFileOperationExecutor.executeOperation(() -> "Saving file file1.txt");
상용구 코드의 양을 줄였기 때문에 이제 구현이 훨씬 더 간소화되고 간결해 보입니다 .
그럼에도 불구하고 질문은 여전히 유효합니다. 이 접근 방식이 객체 지향 접근 방식에 비해 더 나은가요?
음, 까다롭습니다. 더 간결한 코드가 대부분의 경우 더 나은 코드를 의미한다고 가정한다면 실제로 그렇습니다.
일반적으로 람다 식을 사용해야 하는 경우 사용 사례별로 평가해야 합니다 .
3.2. 메서드 참조 사용
마찬가지로 명령 개체를 호출자에 전달하기 위해 메서드 참조를 사용할 수 있습니다.
TextFileOperationExecutor textFileOperationExecutor
= new TextFileOperationExecutor();
TextFile textFile = new TextFile("file1.txt");
textFileOperationExecutor.executeOperation(textFile::open);
textFileOperationExecutor.executeOperation(textFile::save);
이 경우 구현은 여전히 TextFile 인스턴스 를 만들어야 하므로 lambdas 를 사용하는 것보다 조금 더 장황 합니다.
4. 결론
이 기사에서는 명령 패턴의 주요 개념과 개체 지향 접근 방식 및 람다 식과 메서드 참조의 조합을 사용하여 Java에서 패턴을 구현하는 방법을 배웠습니다.
늘 그렇듯이 이 예제에 표시된 모든 코드 예제는 GitHub 에서 사용할 수 있습니다 .