[Design Pattern] Command Pattern
Command Pattern에 대하여 알아보고 , 자바 코틀린 예제를 보고 확인해보도록 하겠습니다.
Command Pattern이란??
실행될 기능을 캡슐화함으로써 주어진 여러 기능을 실행할 수 있는 재사용성이 높은 클래스를 설계하는 패턴
즉, 이벤트가 발생했을 때 실행될 기능이 다양하면서도 변경이 필요한 경우에 이벤트를 발생시키는 클래스를 변경하지 않고 재사용하고자 할 때 유용합니다.
실행될 기능을 캡슐화함으로써 기능의 실행을 요구하는 호출자(Invoker) 클래스와
실제 기능을 실행하는 수신자(Receiver) 클래스 사이의 의존성을 제거합니다.
따라서 실행될 기능의 변경에도 호출자 클래스를 수정 없이 그대로 사용할 수 있도록 줍니다.
클래스가 수행하는 작업
- Command
- 실행될 기능에 대한 인터페이스
- 실행될 기능을 execute 메서드로 선언함
- ConcreteCommand
- 실제로 실행되는 기능을 구현
- 즉, Command라는 인터페이스를 구현함
- Invoker
- 기능의 실행을 요청하는 호출자 클래스
- Receiver
- ConcreteCommand에서 execute 메서드를 구현할 때 필요한 클래스
- 즉, ConcreteCommand의 기능을 실행하기 위해 사용하는 수신자 클래스
Command Pattern with Java
public interface Command { public abstract void execute(); }
invoker
public class Button {
private Command theCommand;
public Button(Command theCommand) {
setCommand(theCommand);
}
public void setCommand(Command newCommand) {
this.theCommand = newCommand;
}
public void pressed() {
theCommand.execute();
}
}
Receiver
public class Alarm {
public void start() {
System.out.println("Alarming");
}
}
public class Lamp {
public void turnOn() {
System.out.println("Lamp On");
}
}
ConcreteCommand
public class AlarmStartCommand implements Command {
private Alarm theAlarm;
public AlarmStartCommand(Alarm theAlarm) {
this.theAlarm = theAlarm;
}
public void execute() {
theAlarm.start();
}
}
public class LampOnCommand implements Command {
private Lamp theLamp;
public LampOnCommand(Lamp theLamp) {
this.theLamp = theLamp;
}
public void execute() {
theLamp.turnOn();
}
}
Command Pattern with Kotlin
ex1) Order Dish
command -> typealias Order
ConcreteCommand -> cookStarter, cookMainCourse, cookDessert
typealias Order = () -> String
fun cookStarter(name: String): Order = { "$name Starter" }
fun cookMainCourse(name: String): Order = { "$name Main Course" }
fun cookDessert(name: String): Order = { "$name Dessert" }
Invoker
class Waiter(private val pendingOrders: MutableList<Order> = mutableListOf()) {
fun acceptOrder(vararg orders: Order) = pendingOrders.addAll(orders)
fun serveOrders(): List<String> = pendingOrders.map { it() }
}
Receiver -> Client(고객)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class CommandTest {
@Test
@DisplayName("Given a set of dishes and a waiter. When order dishes. Then serve ordered dishes")
fun givenSetOfDishesAndWaiter_whenOrderDishes_thenServeOrderedDishes() {
// Given
val starter: Order =
cookStarter("Salad")
val mainCourse: Order =
cookMainCourse("Chicken")
val dessert: Order =
cookDessert("Fruit")
val waiter = Waiter()
// When
waiter.acceptOrder(starter, mainCourse, dessert)
val dishes = waiter.serveOrders()
// Then
val expectedDishes = listOf("Salad Starter", "Chicken Main Course", "Fruit Dessert")
assertThat(dishes, CoreMatchers.equalTo(expectedDishes))
}
}
ex2) Order and Pay
command
interface OrderCommand {
fun execute()
}
ConcreteCommand
class OrderAddCommand(private val id: Long) : OrderCommand {
override fun execute() = println("Adding order with id: $id")
}
class OrderPayCommand(private val id: Long) : OrderCommand {
override fun execute() = println("Paying for order with id: $id")
}
invoker
class CommandProcessor {
private val queue = ArrayList<OrderCommand>()
fun addToQueue(orderCommand: OrderCommand): CommandProcessor =
apply { queue.add(orderCommand) }
fun processCommands(): CommandProcessor =
apply {
queue.forEach { it.execute() }
queue.clear()
}
}
Receiver(Client, 고객)
class CommandTest {
@Test
fun Command() {
CommandProcessor()
.addToQueue(OrderAddCommand(1L))
.addToQueue(OrderAddCommand(2L))
.addToQueue(OrderPayCommand(2L))
.addToQueue(OrderPayCommand(1L))
.processCommands()
}
}
리시버에게 통보
정리
실행될 기능을 캡슐화하고 , 실행될 기능을 커맨드를 통해 실행하는 패턴입니다.
기능이 실행된 후에는 Receiver에 수행된 기능에 대하여 알려줍니다.
Command Pattern으로 구현하게 될 경우 확장에는 열려있고, 변경에는 닫혀있는
즉 기존의 코드를 수정하지 않고 확장할 수 있는 여지가 많아지게 됩니다( OCP를 지킬 수 있습니다.)
샘플 소스 보러 가기
https://github.com/qjatjr1108/DesignPattern
'DesignPattern' 카테고리의 다른 글
[Design Pattern] State Pattern (0) | 2019.10.21 |
---|---|
[Design Pattern] Chain of Responsibility Pattern (0) | 2019.10.21 |
[Design Pattern] Interpreter Pattern (0) | 2019.10.21 |
[Design Pattern] Iterator Pattern (0) | 2019.10.18 |
[Design Pattern] Mediator Pattern (0) | 2019.10.18 |
댓글