Skip to content

Instantly share code, notes, and snippets.

@linearhw
Created January 3, 2018 07:11
Show Gist options
  • Save linearhw/c7a253a441831e279a3ac49ca698bd44 to your computer and use it in GitHub Desktop.
Save linearhw/c7a253a441831e279a3ac49ca698bd44 to your computer and use it in GitHub Desktop.

Command

언제 사용하면 좋은가

  • 객체의 세부 구현을 외부에 보일 필요가 없을 때
  • undo, redo 기능이 필요할 때
  • 큐를 사용하거나 로그를 남기고 싶을 떼

특징

  • Request 를 캡슐화 하기 때문에 로그 요청이나 큐를 클라이언트에 parameter 로 전달할 수 있다
  • 함수를 직접 호출하는 구조가 아니기 때문에 클래스/메서드들이 서로를 자세히 알 필요가 없다

In Swift, ObjC

  • UndoManager: UIApplication.shared.keyWindow.undoManager 로 접근할 수 있다. UITextField 와 UITextView 에서 사용한다.
  • NSInvocation: NSTimer 의 내부 구현을 몰라도 target 과 selector 만 지정해 주면 사용할 수 있다.
[NSTimer timerWithTimeInterval:0.5 invocation:invocation repeats:YES];

예제

키보드 input 에 따라 움직이는 게임 캐릭터 클래스를 만들었다.

class Character {
    enum Button {
        case X, Y, A, B, None
    }
    
    var input: String = "" {
        didSet {
            switch input {
            case "x": pressed = Button.X
            case "y": pressed = Button.Y
            case "a": pressed = Button.A
            case "b": pressed = Button.B
            default: pressed = Button.None
            }
            update()
        }
    }
    private var pressed = Button.None
    
    func update() {
        switch pressed {
        case .A: Fire()
        case .B: Jump()
        case .X: Roll()
        case .Y: Skill()
        case .None: break
        }
    }
    
    func Fire() { }
    func Jump() { }
    func Roll() { }
    func Skill() { }
}

그런데 "유저가 키와 동작을 바꿀 수 있도록 지원해야 한다"는 새로운 스펙이 등장한다면?

캐릭터와 액션의 강한 결합을 없애고 분리할 필요가 있다.

class Action {
    func Fire() { }
    func Jump() { }
    func Roll() { }
    func Skill() { }
}

protocol Command {
    func execute(action: Action)
    // func undo(action: Action)
}

class CommandFire: Command {
    func execute(action: Action) {
        action.Fire()
    }
    
    // func undo(action: Action) {
    //     ...
    // }
}

class CommandJump: Command {
    func execute(action: Action) {
        action.Jump()
    }
}

class CommandRoll: Command {
    func execute(action: Action) {
        action.Roll()
    }
}

class CommandSkill: Command {
    func execute(action: Action) {
        action.Skill()
    }
}


class Character {
    var input: String = ""
    
    private var cFire = CommandFire()
    private var cJump = CommandJump()
    private var cRoll = CommandRoll()
    private var cSkill = CommandSkill()
    private var action = Action()
    
    func update() {
        if let command = getCommand() {
            command.execute(action: action)
        }
    }
    
    func getCommand() -> Command? {
        switch input {
        case "x": return cFire
        case "y": return cJump
        case "a": return cRoll
        case "b": return cSkill
        default: return nil
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment