Java 디자인 패턴-03.Command
(Java 디자인 패턴 스터디 모집 중 : https://github.com/bluedskim/javaDesignPatterns)
(출처:https://refactoring.guru/design-patterns/command)
해결하려는 문제
- view(presentation)과 비즈니스 로직의 직접 연관을 제거(행위가 소스에 하드코딩되어 있다면 런타임에 추가/변경이 불가능)
용도/목적
- 조건문이 많은 복잡한 로직을 간단하게
- invoker와 receiver 사이에 command를 두어 직접 연관을 제거하여 새로운 command가 추가되더라도 invoker를 수정할 필요가 없음
특징
- 행위behavioral 패턴
- 눈에 보이지 않는 무형의 개념(행위)도 객체화 할 수 있다.
고려사항
- command용 interface(TextFileOperation.java)는 @FunctionalInterface로 할 수 있다.
클래스 다이어그램
소스
-
client : command를 초기화하고 invoker를 호출
- TextFileOperationExecutorWithoutCommandTest.java : command를 사용하지 않은 샘플 테스트용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package net.dskim.desingpattern.command;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TextFileOperationExecutorWithoutCommandTest {
@Test
public void executeOperationTest() {
TextFileOperationExceutorWithoutCommand textFileOperationExecutor = new TextFileOperationExceutorWithoutCommand();
String openTextFileResult = textFileOperationExecutor.execute(new TextFile("file1.txt"), "open");
log.info("openTextFileResult={}", openTextFileResult);
assertEquals("Opening file file1.txt", openTextFileResult);
String saveTextFileResult = textFileOperationExecutor.execute(new TextFile("file2.txt"), "save");
log.info("saveTextFileResult={}", saveTextFileResult);
assertEquals("Saving file file2.txt", saveTextFileResult);
}
}
|
- TextFileOperationExecutorWithCommandTest.java : command를 사용한 샘플 테스트용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package net.dskim.desingpattern.command;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TextFileOperationExecutorWithCommandTest {
@Test
public void executeOperationTest() {
TextFileOperationExecutorWithCommand textFileOperationExecutor = new TextFileOperationExecutorWithCommand();
String openTextFileOperationResult = textFileOperationExecutor.executeOperation(new OpenTextFileOperation(new TextFile("file1.txt")));
log.info("openTextFileOperationResult={}", openTextFileOperationResult);
assertEquals("Opening file file1.txt", openTextFileOperationResult);
String saveTextFileOperation = textFileOperationExecutor.executeOperation(new SaveTextFileOperation(new TextFile("file2.txt")));
log.info("saveTextFileOperation={}", saveTextFileOperation);
assertEquals("Saving file file2.txt", saveTextFileOperation);
String udateTextFileOperation = textFileOperationExecutor.executeOperation(new UpdateTextFileOperation(new TextFile("file2.txt")));
log.info("udateTextFileOperation={}", udateTextFileOperation);
assertEquals("Updating file file2.txt", udateTextFileOperation);
}
}
|
-
invoker : command를 받아 interface에 정의된 메소드를 호출하고 필요 시 이력 저장용 list에 저장
- TextFileOperationExecutorWithCommand.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package net.dskim.desingpattern.command;
import java.util.ArrayList;
import java.util.List;
/**
* command 패턴 샘플 invoker 클래스
* 새로운 처리가 추가 되더라도 이 소스를 수정할 필요 없음
*/
public class TextFileOperationExecutorWithCommand {
/**
* 실행 이력 저장용. 이 샘플에서는 불필요
*/
private final List<TextFileOperation> textFileOperations = new ArrayList<>();
public String executeOperation(TextFileOperation textFileOperation) {
textFileOperations.add(textFileOperation);
return textFileOperation.execute();
}
}
|
-
receiver : 실제 로직이 정의된 클래스
- TextFile.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package net.dskim.desingpattern.command;
public class TextFile {
private String name;
public TextFile(String name) {
this.name = name;
}
public String open() {
return "Opening file " + name;
}
public String save() {
return "Saving file " + name;
}
public String update() {
return "Updating file " + name;
}
}
|
-
command : command 객체
- TextFileOperation.java : command용 interface
1
2
3
4
5
6
7
8
9
|
package net.dskim.desingpattern.command;
/**
* command용 interface
*/
@FunctionalInterface
public interface TextFileOperation {
String execute();
}
|
- OpenTextFileOperation.java : 파일 open하기 command
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package net.dskim.desingpattern.command;
/**
* 파일열기 command
*/
public class OpenTextFileOperation implements TextFileOperation {
private TextFile textFile;
public OpenTextFileOperation(TextFile textFile) {
this.textFile = textFile;
}
@Override
public String execute() {
return textFile.open();
}
}
|
- SaveTextFileOperation.java : 파일 save하기 command
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package net.dskim.desingpattern.command;
/**
* 파일저장 command
*/
public class SaveTextFileOperation implements TextFileOperation {
private TextFile textFile;
public SaveTextFileOperation(TextFile textFile) {
this.textFile = textFile;
}
@Override
public String execute() {
return textFile.save();
}
}
|
참고