[์ค์ํํธ ๋์์ธํจํด] ์ปค๋งจ๋ ํจํด(Command Pattern)
์ปค๋งจ๋ ํจํด(Command Pattern)
The command pattern encapsulate a request as an object, thereby letting you parameterize other objects different requests queue or log request and support undoable operation
์ปค๋งจ๋ ํจํด์ ์ด๋ค ๊ฐ์ฒด๋ก ๋ณด๋ด๋ ์์ฒญ์ ์บก์ํํ๋ ํจํด์ ๋๋ค. ๋ฐ๋ผ์ ์์ฒญ์ ๋ณด๋ด๋ ๊ฐ์ฒด๋, ์์ฒญ์ ๋ฐ๋ ๊ฐ์ฒด์ ์๊ด์์ด ์์ฒญ ์์ฒด๋ฅผ ๊ฐ์ฒด๋ก ์บก์ํ ํ๋ ํจํด์ด์ฃ . ์ด๋ ๊ฒ ์์ฒญ์ ์บก์ํ ํ๋ค๋ฉด, ์ด๋ค ์์ ์ ๋ํ ์์ฒญ๋ค์ ๊ฐ์ฒด๋ก ๋ง๋ค์ด ๋๊ณ , ํ์์ ๋ฐ๋ผ์ ๊ฐ์ฒด์๊ฒ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. ์์ฒญ์ ๋ํ ๊ฐ์ฒด๋ค์ ๋ฆฌ์คํธ์ ๋ด์๋๋ฉด ์ด๋ค ์ ์ฐจ์ ์์ ๋ค์ ์์๋๋ก ์ฒ๋ฆฌํ๋ ๊ฒ๋ ๊ฐ๋ฅํฉ๋๋ค. ํน์, ์ด๋ค ์์ ๋ค์ด ๋ฐ๋์ ์์๋๋ก ์ฒ๋ฆฌ๋์ด์ผ ํ๊ณ , ์ด ์์๊ฐ ์ค๋ณต์ ์ผ๋ก ์ฌ์ฉ๋๋ค๋ฉด, ์์ฒญ ๊ฐ์ฒด๋ค์ ํฌํจํ๋ ํ๋์ ํฐ ์์ฒญ ๊ฐ์ฒด๋ฅผ ๋ง๋ค ์ ์๊ฒ ์ฃ .
๋, ์ ์์์๋ support undoable operation ์ด๋ผ๋ ํํ์ ์ฌ์ฉํ๋๋ฐ์, ์์ฒญ ๊ฐ์ฒด๋ค์ ์์ฒญ์ ์ญ์ผ๋ก ์ํํ๋(undo) ์์ ์ ํฌํจํ๊ธฐ ๋๋ฌธ์ ์์ฐจ์ ์ผ๋ก ์คํํ๋ ์์ฒญ ๊ฐ์ฒด๋ค์ ์ญ์์ผ๋ก ์คํํ๋ฉด ์์ ์ ์ทจ์ํ๋ ์์ฒญ์ ๋ง๋๋ ๊ฒ๋ ๊ฐ๋ฅํด์ง๋๋ค.
์ปค๋งจ๋ ํจํด ์ดํดํ๊ธฐ
์ปค๋งจ๋ ํจํด์ ์ดํดํ๊ธฐ ์ํด์ ์ปค๋งจ๋ ํจํด์ ๊ตฌ์ฑํ๋ ๊ตฌ์ฑ์์๋ฅผ ์ ๋ฆฌํด๋ณด๊ฒ ์ต๋๋ค.
์ปค๋งจ๋ ํจํด์ ํฌ๊ฒ ์์ฒญ์ ๋ณด๋ด๋ Invoker, ์์ฒญ์ ์บก์ํํ Command์ ๊ทธ ์ธํฐํ์ด์ค์ธ Commanding, ๊ทธ๋ฆฌ๊ณ ์์ฒญ ๊ฐ์ฒด์ ๋ก์ง์ ํตํด ์ํฅ์ ๋ฐ๋ Receiver ๋ก ๊ตฌ์ฑ์ด ๋ฉ๋๋ค. Invoker๋ ์ํฉ์ ๋ฐ๋ผ ์ฌ๋ฌ๊ฐ์ Command๋ฅผ ๊ฐ์ง ์ ์์ฃ .
๊ทธ๋ฆฌ๊ณ ๊ฐ ๊ฐ์ฒด๋ค์ ๋ค์๊ณผ ๊ฐ์ด ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ฒ ๋ฉ๋๋ค.
Invoker๋ ์ด ๊ฐ์ฒด์์ ์ฌ์ฉํ ์์ฒญ ๊ฐ์ฒด์ธ Command ํ์ ์ ๊ฐ์ฒด๋ฅผ ์์ฑ์๋ฅผ ํตํด ์ฃผ์ ๋ฐ์ ์ ์๋๋ก ๋์ด ์์ด์. ๊ทธ๋ฆฌ๊ณ Command์ ์ธํฐํ์ด์ค์ธ Commanding์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ execute ๋ฉ์๋, ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ฒ๋ฆฌ๋ฅผ ์ญ์ผ๋ก ์ํํ๋ unexecute ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ฒ ๋ฉ๋๋ค. ์ญ์ผ๋ก ์ํํ๋ค๋ ๋ง์ ์ ์์์ ๋ค๋ฃจ์๋ undo ๋ฅผ ์๋ฏธํด์.
๋ง์ฝ ์ฐ๋ฆฌ๊ฐ TV ๋ฆฌ๋ชจ์ปจ์ ์ปค๋งจ๋ ํจํด์ผ๋ก ๊ตฌํํ๋ค๊ณ ํ๋ค๋ฉด, Command ์ค ํ๋๋ ์ ์์ ํค๋ ๋์์ ๋ํ ์์ฒญ์ด ๋ ์ ์๊ฒ ์ฃ ? ์ด๋ execute๋ ์ ์์ ํค๋ ๋ฉ์๋๊ฐ ๋ ๊ฒ์ด๊ณ , unexecute๋ ์ ์์ ์ข ๋ฃํ๋ ๋ฉ์๋๊ฐ ๋ ๊ฑฐ์์.
๊ทธ๋ผ ๊ฐ๋จํ TV ๋ฆฌ๋ชจ์ปจ ์ปจ์ ์ ์ปค๋งจ๋ ํจํด์ผ๋ก ๋ง๋ค๋ฉด์ ํ์คํ๊ฒ ์ดํด๋ฅผ ํด๋ณด๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค์.
TV ๋ฆฌ๋ชจ์ปจ ๊ตฌํํ๊ธฐ
๋จผ์ ์ปค๋งจ๋๋ฅผ ์ฌ์ฉํ๊ณ ์ํฅ์ ๋ฐ๊ฒ๋ Invoker์ Receiver ๋ถํฐ ์ ์ํด๋ณผ๊ฒ์. ๋ฆฌ๋ชจ์ปจ์ ์ปค๋งจ๋๋ TV๋ฅผ ๋๋ค, ์ผ ๋ค, ์ฑ๋์ ์ด๋ํ๋ค, ์ด์ ์ฑ๋๋ก ์ด๋ํ๋ค ์ด๋ฐ ๊ฒ๋ค์ด ๋๊ฒ ์ฃ ? ๊ทธ๋ผ ์ปค๋งจ๋๋ฅผ ์ฌ์ฉํ๋ ๋ฆฌ๋ชจ์ปจ์ Invoker๋ก ๋๊ณ , ์ํฅ์ ๋ฐ๊ฒ๋๋ TV๋ฅผ Receiver๋ก ์ ์ํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค์.
TV๊ฐ ์ํํ๊ฒ๋ ๋์์ ์์์ ๋์ดํ๋ ๊ฒ ์ฒ๋ผ, ๋๊ธฐ, ์ผ๊ธฐ, ์๋ก ํ ์ฑ๋ ์ด๋, ์๋๋ก ํ ์ฑ๋ ์ด๋์ผ๋ก ์ค์ ํ๊ฒ ์ต๋๋ค.
final class TVRemoteControl {
private let onCommand: Commanding
private let offCommand: Commanding
private let upCommand: Commanding
private let downCommand: Commanding
init(
on: Commanding,
off: Commanding,
up: Commanding,
down: Commanding
) {
self.onCommand = on
self.offCommand = off
self.upCommand = up
self.downCommand = down
}
func on() {
self.onCommand.execute()
}
func off() {
self.offCommand.execute()
}
func up() {
self.upCommand.execute()
}
func down() {
self.downCommand.execute()
}
}
๊ทธ๋ผ ์ด๋ ๊ฒ ๊ตฌํํ ์ ์๊ฒ ์ฃ .
์ฐ๋ฆฌ๋ ์ปค๋งจ๋ ํจํด์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ฆฌ๋ชจ์ปจ์ด ์ง์ TV๋ฅผ ์กฐ์ํ๊ฒ ํ์ง ์๊ณ , ๋ฆฌ๋ชจ์ปจ์ด ๊ฐ์ง๊ฒ๋ ์ปค๋งจ๋๋ฅผ ํตํด TV๋ฅผ ์กฐ์ํ๊ฒ ๋ฉ๋๋ค. ๋ฐ๋ผ์ ๊ฐ ๋์์ ๋ํ ์์ฒญ์ ๊ฐ์ฒด๋ก ์บก์ํํ๋ ค๋ฉด ์ด ๋ค ๊ฐ์ ์ปค๋งจ๋ ๊ฐ์ฒด๊ฐ ํ์ํ๊ฒ ๋์ฃ .
๊ทธ๋ฆฌ๊ณ ์ปค๋งจ๋ ๊ฐ์ฒด๋ฅผ ์ง์ ์์ ํ์ง ์๊ณ , ์์ ์ ๋ฆฌํ๋๋๋ก ์ธํฐํ์ด์ค๋ฅผ ์์ ํ๊ฒ ๋ฉ๋๋ค. ์ธํฐํ์ด์ค๋ ๋จ์ํ๊ฒ execute์ unexecute์ ์กฐํฉ์ผ๋ก ์ฝ๊ฒ ์ ์ํ ์ ์์ด์.
protocol Commanding {
func execute()
func unexecute()
}
๊ทธ๋ฆฌ๊ณ on, off, up, down ๊ฐ๊ฐ์ ๋ํ ๊ฐ์ฒด๋ฅผ Commanding์ ์ฑํํ๋ ๊ฐ์ฒด๋ก ๊ตฌํํ๊ฒ ๋ฉ๋๋ค. ์ํฅ์ ๋ฐ์ Recevier๋ ์ด ๊ฐ์ฒด๊ฐ ์์ ํ๊ณ ์์ด์ผ๊ฒ ์ฃ ?
final class OnCommand: Commanding {
let tv: TV
init(tv: TV) {
self.tv = tv
}
func execute() {
tv.on()
}
func unexecute() {
tv.off()
}
}
final class OffCommand: Commanding {
let tv: TV
init(tv: TV) {
self.tv = tv
}
func execute() {
tv.off()
}
func unexecute() {
tv.on()
}
}
final class UpCommand: Commanding {
let tv: TV
init(tv: TV) {
self.tv = tv
}
func execute() {
tv.up()
}
func unexecute() {
tv.down()
}
}
final class DownCommand: Commanding {
let tv: TV
init(tv: TV) {
self.tv = tv
}
func execute() {
tv.down()
}
func unexecute() {
tv.up()
}
}
์ด๋ ๊ฒ ๊ฐ๋จํ๊ฒ ์ปค๋งจ๋ ๊ฐ์ฒด๋ฅผ ์ ์ํ ์ ์์ต๋๋ค. ์ค์ ๋ก ๋์์ ์ํํ๋ ๊ฒ์ ์ฃผ์ ๋ TVํ์ ์ ๊ฐ์ฒด์ด์ง๋ง ํด๋น ๋์์ ๋ํ ์์ฒญ์ ์ปค๋งจ๋ ๊ฐ์ฒด๊ฐ ํ๊ฒ๋๋ ๊ฒ์ด์ฃ .
๊ทธ๋ผ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
let appleTV = TV()
let appleRemoteControl = TVRemoteControl(
on: OnCommand(tv: appleTV),
off: OffCommand(tv: appleTV),
up: UpCommand(tv: appleTV),
down: DownCommand(tv: appleTV)
)
appleRemoteControl.on()
appleRemoteControl.off()
appleRemoteControl.down()
appleRemoteControl.up()
//on
//off
//down
//up
//Program ended with exit code: 0
์ด๋ ๊ฒ Receiver์ธ TV๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ค์ ์ปค๋งจ๋ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ฉฐ TV ์ธ์คํด์ค๋ฅผ ์ฃผ์ ํ๊ณ , ์์ฑ๋ ์ปค๋งจ๋ ์ธ์คํด์ค๋ฅผ ๋ฆฌ๋ชจ์ปจ ์ธ์คํด์ค์ ์ฃผ์ ํ๋ฉด ์ปค๋งจ๋ ํจํด์ ํตํด ๋ฆฌ๋ชจ์ปจ์ผ๋ก TV์ ๋์์ ์์ฒญํ๋๋ก ๊ตฌํ์ด ๋ฉ๋๋ค.
Undo ์ฌ์ฉํ๊ธฐ
์ด๋ฒ์ undo๋ฅผ ์ฌ์ฉํ ์ ์๋ ์ํฉ์ ์๊ฐํด๋ณผ๊ฒ์. ๋ง์ฝ ๋ฆฌ๋ชจ์ปจ์ ํน์ํ ๊ธฐ๋ฅ์ด ์์ด์ ์ง๊ธ๊น์ง ์ํํ๋ ๋ชจ๋ ๋ฆฌ๋ชจ์ปจ ๋์์ ๊ธฐ๋กํ๊ณ ์ญ์ผ๋ก ์ํํ ์ ์๋ค๋ฉด ์ด๋จ๊น์? ๊ตฌํ์ ํด๋ณด๊ฒ ์ต๋๋ค.
final class TVRemoteControl {
private let onCommand: Commanding
private let offCommand: Commanding
private let upCommand: Commanding
private let downCommand: Commanding
private var commandHistory: [Commanding] = []
init(
on: Commanding,
off: Commanding,
up: Commanding,
down: Commanding
) {
self.onCommand = on
self.offCommand = off
self.upCommand = up
self.downCommand = down
}
func on() {
self.onCommand.execute()
self.commandHistory.append(self.onCommand)
}
func off() {
self.offCommand.execute()
self.commandHistory.append(self.offCommand)
}
func up() {
self.upCommand.execute()
self.commandHistory.append(self.upCommand)
}
func down() {
self.downCommand.execute()
self.commandHistory.append(self.downCommand)
}
}
๊ฐ์ฅ ๋จผ์ Commanding ํ์ ์ ๋ด์ ์ ์๋ ๋ฆฌ์คํธ๋ฅผ ํ๋ ๋ง๋ค๊ณ , ์๋ก์ด ์์ฒญ์ ๋ณด๋ผ ๋๋ง๋ค ์์ฒญ์ ๋ํผ๋ฐ์ค๋ฅผ ์ ์ฅํ ์ ์๋๋ก ํ์ด์. ์ฌ์ค enum์ผ๋ก ์์ฒญ์ ํ์ ๋ค์ ๋ง๋ค์ด์ ๋ฐฐ์ด์ ๊ฐํ์ ์ ์ฃผ๋ ๊ฒ์ด ์ ์ผ ์ข๊ฒ ์ง๋ง ์ฌ๊ธฐ์๋ ์ผ๋จ ์ด๋ ๊ฒ ์งํํ ๊ฒ์.
๊ทธ๋ฆฌ๊ณ ์ด์ ์ญ์์ผ๋ก ๋์ ์ทจ์๋ฅผ ์ํํ๊ธฐ ์ํด์ ํ๊ฐ์ง ๋ฉ์๋๋ฅผ ๋ ์ถ๊ฐํ๊ฒ ์ต๋๋ค.
func reverseAll() {
while self.commandHistory.isEmpty == false {
let command = self.commandHistory.removeLast()
command.unexecute()
}
}
๋ฆฌ์คํธ์ ์ ์ฅ๋ ์ปค๋งจ๋๋ค์ ์คํ์ฒ๋ผ ๋ค์์๋ถํฐ ์ ๊ฑฐํ๋ฉด์ unexecute๋ฅผ ํธ์ถํ๋ ๋ฉ์๋์ ๋๋ค.
appleRemoteControl.on()
appleRemoteControl.off()
appleRemoteControl.down()
appleRemoteControl.up()
appleRemoteControl.reverseAll()
//on
//off
//down
//up
//down
//up
//on
//off
//Program ended with exit code: 0
์์์ ์ฌ์ฉํ๋ ์์ ๋์ reverseAll์ ํธ์ถํ๋ฉด, ์ง๊ธ๊น์ง ์ํํ๋ ์์ ๋ค์ ์์ฐจ์ ์ผ๋ก ์ทจ์ํ๋ ๋์์ ํ์ธํ ์ ์๊ฒ ๋ฉ๋๋ค. ๊ฐ๋จํ์ฃ ?
๋ง๋ฌด๋ฆฌ
์ด๋ ๊ฒ ํด์ ์ปค๋งจ๋ ํจํด์ ์ ๋ฆฌํด๋ณด์์ต๋๋ค. iOS์์ ์์ฃผ ์ฌ์ฉํ๋ ์ํคํ ์ฒ ํจํด์ธ MVVM ์์ ๋ทฐ ์ปจํธ๋กค๋ฌ์ ๋ทฐ ๋ชจ๋ธ ์ฌ์ด์ ์์กด์ฑ์ ๋์จํ๊ฒ ํ๊ธฐ ์ํด์ ์ปค๋งจ๋ ํจํด์ ์ฌ์ฉํ ์ ์์ํ ๋ฐ์, ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ ๋ ๋ฆฌ๋ ๋ทฐ ์ปจํธ๋กค๋ฌ๊ฐ Invoker๊ฐ ๋๊ฒ ๊ณ , ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ๋ Receiver๊ฐ ๋ทฐ๋ชจ๋ธ, ๋ทฐ๋ชจ๋ธ์ ์์ ํ๊ณ ํน์ ํ ์ด๋ฒคํธ๋ฅผ ์บก์ํํ Command๋ก ๊ตฌ์ฑํ๋ค๋ฉด, ๋ทฐ ์ปจํธ๋กค๋ฌ์ ๋ทฐ๋ชจ๋ธ์ ์๋ก๋ฅผ ์์ง ๋ชปํ ์ฑ๋ก ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ๊ณ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ํตํด ๋ทฐ๋ฅผ ์ ๋ฐ์ดํธ ํ ์ ์์ ๊ฒ ๊ฐ์ต๋๋ค.
์๋ชป๋ ๋ด์ฉ์ด๋ ํ๋ฆฐ ๋ด์ฉ์ด ์๋ค๋ฉด ์๋ ค์ฃผ์ธ์..! ์ ๋ ๋ค์ ๊ณต๋ถํ๋ฉด์ ๊ณ ๋ฏผํด๋ณด๊ฒ ์ต๋๋ค. ์ค๋๋ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค :)