Command Pattern
Understanding the Command Pattern: A design pattern that encapsulates requests as objects for flexible and maintainable code
September 13, 2024
커맨드 패턴은 소프트웨어 디자인 패턴 중 하나로, 요청을 객체의 형태로 캡슐화하여 사용자가 보낸 요청을 나중에 이용할 수 있도록 매개변수화하고, 큐에 넣거나 로그로 기록하거나 작업 취소 기능을 지원할 수 있게 해줍니다.
커맨드 패턴의 주요 구성 요소
- Command: 실행될 기능에 대한 인터페이스
- ConcreteCommand: Command 구현하는 클래스
- Invoker: 기능의 실행을 요청하는 호출자 클래스
- Receiver: ConcreteCommand에서 실제로 기능을 실행하는 수신자 클래스
제공된 코틀린 코드를 통해 커맨드 패턴의 구현을 살펴보겠습니다.
- Command 추상 클래스
abstract class Command {
abstract fun execute(): String
abstract fun undo(): String
}
이 추상 클래스는 모든 커맨드가 구현해야 할 기본 구조를 정의합니다. execute() 메서드는 커맨드를 실행하고, undo() 메서드는 실행을 취소합니다.
- Receiver 클래스
class Kimchi() {
fun cookKimchi(): String {
return "Cook Kimchi"
}
}
Kimchi 클래스는 실제 작업을 수행하는 Receiver 역할을 합니다.
- ConcreteCommand 클래스
class CommandKimchi(private val kimchi: Kimchi) : Command() {
private val prevCook: String = "None"
override fun execute(): String {
return kimchi.cookKimchi()
}
override fun undo(): String {
return prevCook
}
}
CommandKimchi는 구체적인 커맨드를 구현합니다. 이 클래스는 Kimchi 객체를 받아 실제 작업을 수행합니다.
- NoCommand 클래스
class NoCommand : Command() {
override fun execute(): String {
return "No command"
}
override fun undo(): String {
return "No command"
}
}
NoCommand는 Null Object 패턴을 구현한 것으로, 커맨드가 설정되지 않았을 때의 기본 동작을 정의합니다.
- Invoker 클래스
class InvokeCommand : Command() {
private var commend: Command = NoCommand()
fun setCommend(command: Command) {
commend = command
}
override fun execute(): String {
return commend.execute()
}
override fun undo(): String {
return commend.undo()
}
}
InvokeCommand는 Invoker 역할을 합니다. 이 클래스는 실제 커맨드 객체를 가지고 있으며, 클라이언트의 요청에 따라 해당 커맨드를 실행하거나 취소합니다.
- 테스트 코드
class CommandPatternTest {
@Test
fun testInvokeCommand() {
val invoker = InvokeCommand()
// 초기 상태 테스트 (NoCommand)
assertEquals("No command", invoker.execute())
assertEquals("No command", invoker.undo())
// Kimchi 커맨드 설정 및 테스트
val kimchi = Kimchi()
val kimchiCommand = CommandKimchi(kimchi)
invoker.setCommend(kimchiCommand)
assertEquals("Cook Kimchi", invoker.execute())
assertEquals("None", invoker.undo())
}
}
커맨드 패턴의 장점
- 확장성: 새로운 커맨드를 추가하기 쉽습니다.
- 단일 책임 원칙: 각 커맨드 클래스는 하나의 작업만 담당합니다.
- 개방-폐쇄 원칙: 기존 코드를 수정하지 않고 새로운 커맨드를 추가할 수 있습니다.
- 실행 취소와 재실행: undo() 메서드를 통해 작업 취소 기능을 쉽게 구현할 수 있습니다.
- 요청의 큐잉과 로깅: 커맨드 객체를 저장하거나 로그로 남길 수 있어 복잡한 연산을 관리하기 용이합니다.
커맨드 패턴은 요청을 객체로 캡슐화하여 클라이언트와 수신자 사이의 의존성을 줄이고, 유연한 시스템 설계를 가능하게 합니다. 이 패턴을 통해 개발자는 복잡한 작업을 단순한 인터페이스로 추상화하고, 시스템의 확장성과 유지보수성을 향상시킬 수 있습니다.