자바의 명령 패턴

1. 개요

명령 패턴은 동작 디자인 패턴이며 GoF의 공식적인 디자인 패턴 목록의 일부입니다. 간단히 말해, 패턴은 호출 할 메서드, 메서드의 인수 및 메서드가 속한 개체를 포함하여 주어진 작업 (명령)을 수행하는 데 필요한 모든 데이터를 개체캡슐화 하려고합니다 .

이 모델을 사용하면 소비자로부터 명령을 생성하는 객체분리 할 수 있으므로 패턴이 일반적으로 생산자-소비자 패턴으로 알려진 이유입니다.

이 튜토리얼에서는 객체 지향 및 객체 기능 접근법을 모두 사용하여 Java에서 명령 패턴을 구현하는 방법을 배우고 어떤 사용 사례에서 유용 할 수 있는지 알아 봅니다.

2. 객체 지향 구현

클래식 구현에서 명령 패턴은 Command, Receiver, Invoker 및 Client의 네 가지 구성 요소를 구현 해야합니다 .

패턴이 작동하는 방식과 각 구성 요소가 수행하는 역할을 이해하기 위해 기본 예제를 만들어 보겠습니다.

텍스트 파일 애플리케이션을 개발하고 싶다고 가정 해 보겠습니다. 이 경우 텍스트 파일 열기, 쓰기, 저장 등과 같은 일부 텍스트 파일 관련 작업을 수행하는 데 필요한 모든 기능을 구현해야합니다.

따라서 위에서 언급 한 네 가지 구성 요소로 응용 프로그램을 분류해야합니다.

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를 정의하고 두 구현 인 OpenTextFileOperationSaveTextFileOperation 이 구체적인 작업을 수행합니다. 전자는 텍스트 파일을 열고 후자는 텍스트 파일을 저장합니다.

명령 개체의 기능은 명확합니다. 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. Invoker 클래스

호출자는 주어진 명령을 실행하는 방법을 알고 있지만 명령이 어떻게 구현되었는지는 모르는 객체입니다 . 명령의 인터페이스 만 알고 있습니다.

경우에 따라 호출자는 명령을 실행하는 것 외에도 명령을 저장하고 큐에 넣습니다. 이는 매크로 기록 또는 실행 취소 및 다시 실행 기능과 같은 일부 추가 기능을 구현하는 데 유용합니다.

이 예제에서는 명령 객체를 호출하고 명령의 execute () 메서드를 통해 실행하는 추가 구성 요소가 있어야 함이 분명해집니다 . 이것이 바로 invoker 클래스가 작동하는 곳 입니다.

호출자의 기본 구현을 살펴 보겠습니다.

public class TextFileOperationExecutor { private final List 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"); 

The implementation now looks much more streamlined and concise, as we've reduced the amount of boilerplate code.

Even so, the question still stands: is this approach better, compared to the object-oriented one?

Well, that's tricky. If we assume that more compact code means better code in most cases, then indeed it is.

As a rule of thumb, we should evaluate on a per-use-case basis when to resort to lambda expressions.

3.2. Using Method References

Similarly, we can use method references for passing command objects to the invoker:

TextFileOperationExecutor textFileOperationExecutor = new TextFileOperationExecutor(); TextFile textFile = new TextFile("file1.txt"); textFileOperationExecutor.executeOperation(textFile::open); textFileOperationExecutor.executeOperation(textFile::save); 

In this case, the implementation is a little bit more verbose than the one that uses lambdas, as we still had to create the TextFile instances.

4. Conclusion

이 기사에서 우리는 명령 패턴의 핵심 개념과 객체 지향 접근 방식과 람다 식과 메서드 참조의 조합을 사용하여 Java에서 패턴을 구현하는 방법을 배웠습니다.

평소처럼이 자습서에 표시된 모든 코드 예제는 GitHub에서 사용할 수 있습니다.