GoF์ ๋์์ธ ํจํด, ํด์์ ํจํด์ ๋ํด ์์๋ณธ๋ค.
ํด๋น ๊ธ์, ๋ค์์ ์ฝ๋ ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ดํดํ๋ ๊ฒ์ด ํธ๋ฆฌํฉ๋๋ค.
ํต์ฌ ์์ฝ
๋ฌธ๋ฒ์ ๋ง์ถฐ ์์ฑ๋ ์คํฌ๋ฆฝํธ๋ฅผ ํด์
ํด์๋ ๊ตฌ๋ฌธ์ ์ ํด์ง ๊ท์น๋๋ก ์คํํ๋ ํจํด
์์
Script: BEGIN FRONT LOOP 2 BACK RIGHT END BACK END
์ฝ๊ฒ ๋ณด๊ธฐ
BEGIN // ์คํฌ๋ฆฝํธ ์์
FRONT // [๋ช
๋ น] ์์ผ๋ก ๊ฐ๊ธฐ
LOOP 2 // ๋ฐ๋ณต๋ฌธ ์์, ๋ฐ๋ณต ํ์
BACK // [๋ช
๋ น] ๋ค๋ก ๊ฐ๊ธฐ
RIGHT // [๋ช
๋ น] ์ค๋ฅธ์ชฝ์ผ๋ก ๊ฐ๊ธฐ
END // ์คํฌ๋ฆฝํธ ๋
BACK // [๋ช
๋ น] ๋ค๋ก ๊ฐ๊ธฐ
END // ์คํฌ๋ฆฝํธ ๋
Expression: BEGIN FRONT LOOP 2 BACK RIGHT END BACK END ์์ ํํ๋ ๊ฐ ํ ๋จ์ด
Context: Expression์ ์ ํด์ง ๋ฌธ๋ฒ์ ๋ง์ถฐ ํด์๋ ๊ฒฐ๊ณผ
Context
: ์คํฌ๋ฆฝํธ์์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ด
Expression
: ์คํฌ๋ฆฝํธ๋ฅผ ๊ตฌ์ฑํ๋ ๊ฐ ๊ตฌ๋ฌธ์ ์ฒ๋ฆฌ
BeginExpression
: BEGIN
๊ตฌ๋ฌธ์ ์ฒ๋ฆฌํ๋ Expression
CommandListExpression
: ์ฌ๋ฌ๊ฐ์ CommandExpression
์ ๊ฐ์ง ์ ์์
CommandExpression
: ์ค์ ์คํํ ์ ์๋ ๋ช
๋ น์ ๋ํ ๊ตฌ๋ฌธ (LOOP
, BACK
etc)์ ๋ํ๋ด๋ ์ธํฐํ์ด์ค
LoopCommandExpression
: ๋ฐ๋ณต๋ฌธ ๋ฃจํ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ตฌ๋ฌธ
ActionCommandExpression
: FRONT
, BACK
, RIGHT
, LEFT
์ ๋์์ ์ฒ๋ฆฌํ๋ ๊ตฌ๋ฌธ
Code
main
import Foundation
internal func main () {
let script = "BEGIN FRONT LOOP 2 BACK RIGHT END BACK LOOP 4 BACK FRONT LEFT END LEFT END"
let context = Context ( script : script)
let expression = BeginExpression ()
if expression. parse ( context : context) {
print (expression. description )
}
}
main ()
script๋ฅผ ๋ฐ์ context์ ๋ฃ์ด ์ด๋ฅผ ๋ถ์ํ๋ค.
์ค์ ํ์์ ๊ธฐ๋ฅ์ด ๊ตฌ๋ถ๋ expression์ ์ด context๋ฅผ ํ๊ณ ํ๊ณ ๋๊ธฐ๋ฉด์ ๋ถ์์ ์งํํ๋ค.
์์์ BeginExpression์ด ๋ ๊ฒ์ด๋ฏ๋ก, ์ฌ๊ธฐ์ ๋ถ์๋ Context๋ฅผ ๋ฃ์ด ํ์ฑ ๊ธฐ๋ฅ์ ๋์์ํจ๋ค.
Context
import Foundation
internal class Context {
private ( set ) var currentKeyword: String ?
internal init ( script : String ) {
self .tokenizer = Tokenizer ( script : script)
self . readNextKeyword ()
}
internal func readNextKeyword () {
self .currentKeyword = self .tokenizer.nextToken
}
private let tokenizer: Tokenizer
}
internal class Tokenizer {
internal init ( script : String ) {
self .tokens = script. components ( separatedBy : .whitespaces)
}
internal var nextToken: String ? {
guard self .tokens. isEmpty == false else {
return nil
}
return self .tokens. removeFirst ()
}
private var tokens: [ String ]
}
Context
๋ ์ค์ ๋ฌธ์์ด์ ํ ํฐ์ผ๋ก ๋๋ ์ฃผ๋ ํ ํฌ๋์ด์ ๋ฅผ ๊ฐ๋๋ค.
Context
๋ ์ธ๋ถ์์ ์ฝ๊ฒ ๋ค์ ํ ํฐ์ ์ป๊ธฐ ์ํ Wrapping ํด๋์ค๋ผ ์๊ฐํ๋ฉด ๋๊ฒ ๋ค.
Expression
import Foundation
internal protocol Expression : Loggable {
func parse ( context : Context) -> Bool
func run () -> Bool
}
internal protocol KeywordAcceptable {
static func isValid ( keyword : String ) -> Bool
}
internal protocol Loggable {
var description: String { get }
}
์ค์ ํ์์ ๊ธฐ๋ฅ์ด ๋ด๊ธธ ์ธํฐํ์ด์ค์ด๋ค.
๊ธฐ๋ฅ์ด ๋ด๊ฒจ์๊ธฐ ๋๋ฌธ์ Command ํจํด์ ์ผ์ข
์ด๋ผ ๋ณด์๋ ๋ฌด๋ฐฉํ๋ค.
parse
๋ Context
๋ฅผ ๋ฐ์ ์์ ์ด ์ฒ๋ฆฌํ ์ ์๋์ง ํ์ธํ๊ณ ,
๊ทธ์ ๋ง๋ ํ์ expression์ ๋ง๋๋ ์ฑ
์์ ๊ฐ๋๋ค.
run
์ ๋ง๋ค์ด์ง ๋ค์ expression๋ค์ ๋ํด ๋์์ ์คํํ๊ณ ์ ํํ๋ ์ญํ ์ ํ๋ค.
KeywordAcceptable
์ ํ์ ๊ธฐ๋ฅ์ค์ ๊ตฌ๋ฌธ๊ณผ ์ฆ๊ฐ ๋์๋๋ Expression์ ๋ํด ์ด๋ฅผ ์ ์ํด์ฃผ๊ธฐ ์ํด ๋ง๋ค์๋ค.
BeginExpression
import Foundation
internal class BeginExpression : Expression {
internal func parse ( context : Context) -> Bool {
// ๋ด ํค์๋๊ฐ ๋ง๋์ง ํ์ธ
guard let keyword = context.currentKeyword,
Self . isValid ( keyword : keyword) else {
return false
}
// ํ์ Expression ์์ฑ
self .expression = CommandListExpression ()
// ๋ค์์ผ๋ก ๋๊ธฐ๊ธฐ ์ Context ํ์ฒ๋ฆฌ
context. readNextKeyword ()
guard let expression else {
return false
}
return expression. parse ( context : context);
}
internal func run () -> Bool {
guard let expression else {
return false
}
return expression. run ()
}
private var expression: CommandListExpression ?
}
extension BeginExpression : KeywordAcceptable {
internal static func isValid ( keyword : String ) -> Bool {
keyword == "BEGIN"
}
}
extension BeginExpression : Loggable {
internal var description: String {
"BEGIN " + "[" + ( self .expression ? . description ?? "" ) + "]"
}
}
CommandListExpression
import Foundation
internal class CommandListExpression : Expression {
internal func parse ( context : Context) -> Bool {
var result: Bool = true
while true {
guard let keyword = context.currentKeyword else {
result = false
break
}
guard keyword != "END" else {
context. readNextKeyword ()
break
}
guard let command = self . determineCommand ( with : keyword),
command. parse ( context : context) else {
result = false
break
}
self .commands. append (command)
}
return result
}
internal func run () -> Bool {
for command in self .commands {
guard command. run () else {
return false
}
}
return true
}
// ์๋๋ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ํ๋๊ฒ ์ข์๋ฐ ๊ทธ๋ฅ ๋์ถฉ ํจ
private func determineCommand ( with keyword: String ) -> CommandExpression ? {
let command: CommandExpression ?
if LoopCommandExpression. isValid ( keyword : keyword) {
command = LoopCommandExpression ( keyword : keyword)
} else if ActionCommandExpression. isValid ( keyword : keyword) {
command = ActionCommandExpression ( keyword : keyword)
} else {
command = nil
}
return command
}
private var commands = [CommandExpression]()
}
extension CommandListExpression : Loggable {
internal var description: String {
self .commands. map { $0 . description }. joined ( separator : " " )
}
}
CommandExpression
import Foundation
internal protocol CommandExpression : Expression , KeywordAcceptable {
var keyword: String { get }
}
Command์ ๊ฒฝ์ฐ์๋ ํญ์ ํค์๋์ ๋์๋๋ ๊ธฐ๋ฅ์ ๊ฐ์ง ์ ๋ฐ์ ์๋ค.
๊ทธ๋ ๊ธฐ์ ํญ์ ํค์๋๋ฅผ ๊ฐ์ง ์ ์๋๋ก ํ๋ค.
LoopCommandExpression
import Foundation
internal class LoopCommandExpression : CommandExpression {
internal let keyword: String
internal var count: Int ?
internal init ( keyword : String ) {
self .keyword = keyword
}
internal func parse ( context : Context) -> Bool {
guard Self . isValid ( keyword : self .keyword) else {
return false
}
context. readNextKeyword ()
guard let count = context.currentKeyword else {
return false
}
self . count = Int (count)
context. readNextKeyword ()
guard context.currentKeyword != nil else {
return false
}
self .expression = CommandListExpression ()
guard let expression else {
return false
}
return expression. parse ( context : context)
}
internal func run () -> Bool {
guard let count, let expression else {
return false
}
for _ in ( 0 ..< count) {
guard expression. run () else {
return false
}
}
return true
}
private var expression: CommandListExpression ?
}
extension LoopCommandExpression : KeywordAcceptable {
internal static func isValid ( keyword : String ) -> Bool {
keyword == "LOOP"
}
}
extension LoopCommandExpression : Loggable {
internal var description: String {
"LOOP( \( self . count ?? 0 ) )" + "{" + ( self .expression ? . description ?? "" ) + "}"
}
}
ActionCommandExpression
import Foundation
internal class ActionCommandExpression : CommandExpression {
internal let keyword: String
internal init ( keyword : String ) {
self .keyword = keyword
}
internal func parse ( context : Context) -> Bool {
guard Self . isValid ( keyword : self .keyword) else {
return false
}
context. readNextKeyword ()
guard context.currentKeyword != nil else {
return false
}
return true
}
internal func run () -> Bool {
print ( "cmd: \( self . keyword ) " )
return true
}
}
extension ActionCommandExpression : KeywordAcceptable {
internal static func isValid ( keyword : String ) -> Bool {
[ "FRONT" , "BACK" , "LEFT" , "RIGHT" ]. contains (keyword)
}
}
extension ActionCommandExpression : Loggable {
internal var description: String {
self .keyword
}
}
๊ฒฐ๊ณผ
BEGIN [FRONT LOOP(2){BACK RIGHT} BACK LOOP(4){BACK FRONT LEFT} LEFT]
cmd: FRONT
cmd: BACK
cmd: RIGHT
cmd: BACK
cmd: RIGHT
cmd: BACK
cmd: BACK
cmd: FRONT
cmd: LEFT
cmd: BACK
cmd: FRONT
cmd: LEFT
cmd: BACK
cmd: FRONT
cmd: LEFT
cmd: BACK
cmd: FRONT
cmd: LEFT
cmd: LEFT
ํ์ฉ์ฑ
ADT(Abstract Syntax Tree)๋ก์ ํน์ ์ธ์ด์ ๋ฌธ์ฅ์ ํํํ๊ณ ์ ํ ๋ ์ฌ์ฉํ๋ฉด ์ข๋ค.
์ ์ํ ์ธ์ด์ ๋ฌธ๋ฒ์ด ๊ฐ๋จํ ๊ฒฝ์ฐ
ํจ์จ์ฑ์ ๊ณ ๋ คํ ํ์๊ฐ ์์ ๊ฒฝ์ฐ
๊ฒฐ๊ณผ
์ฅ์
๋ฌธ๋ฒ์ ๋ณ๊ฒฝ๊ณผ ํ์ฅ์ด ์ฝ๋ค.
๋ฌธ๋ฒ์ ๊ตฌํ์ด ์ฉ์ดํ๋ค
๋จ์
๋ณต์กํ ๋ฌธ๋ฒ์ ๊ด๋ฆฌํ๊ธฐ ์ด๋ ต๋ค.
์๊ฐํด๋ณผ ์
ํ์์ ๊ตญํ๋์ด ์ฌ์ฉํ๊ธฐ ์ข์๋ณด์ด๋ ํจํด์ด๋ค.
์ด๊ฑธ ํจํด์ด๋ผ ํ ์ ์๋์ง๋ ์ฝ๊ฐ ์๋ฌธ์ด ๋ ๋ค.
17. Command ํจํด์ ์์ฉ์ด๋ผ๊ณ ๋ณด๋ ๊ฒ์ด ๋ ์ข์ ๋ฏ
ํ์์ ๊ฐ์ ์ญํ ์ ํ๋ ๋ฌด์ธ๊ฐ๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค๋ฉด ํ๋ฒ์ฏค ์๊ฐํด ๋ณผ๋ง ํ๋ค.
์ปดํ์ผ๋ฌ ๊ตฌํ์ ๋๋ฆฌ ์ฌ์ฉ๋๋ค๊ณ ํ๋ค.
Reference