apple/DesignPattern & Architecture

[Swift] Command Pattern

lgvv 2022. 7. 5. 19:45

Command Pattern

✅ Command Pattern

 

아래의 문서를 구입하여 영어 문서를 번역하고 이해한 것을 바탕으로 글을 작성하고 있습니다.

https://www.raywenderlich.com/books/design-patterns-by-tutorials/v3.0/chapters/21-command-pattern

 

Design Patterns by Tutorials, Chapter 21: Command Pattern

This is a behavioral pattern that encapsulates information to perform an action into a command object. Learn how you can model the concept of executing an action and to use this pattern whenever you want to create actions that can be executed on different

www.raywenderlich.com

 

 

커맨드 패턴은 작업 수행을 위한 정보를 명령 객체로 캡슐화하는 동작 패턴이다. 여기에는 아래와 같은 세가지로 구성된다.

Command Pattern

 

invoker: 커맨드(명령)을 저장하고 실행

command: 작업을 객체로 캡슐화

receivcer: 명령에 의 작동되는 객체

 

따라서 이 패턴을 활용해서 액션을 취했을 때 행동하는 커맨드(명령)들을 모델링 할 수 있다!!

 

When should you use it?

이 패턴은 나중에 receiver에서 실행할 수 있는 작업을 만들 때 사용한다. 예를 들면 컴퓨터 AI가 수행할 명령을 만들고 저장한 다음 시간이 지남에 따라 실행할 수 있음.

 

Playground example

 

Receiver를 만듭니다.

import Foundation

// MARK: - Receiver
public class Door {
  public var isOpen = false
}

Door은 Receiver 역할을 수행할 아주 간단한 모델이빈다. open and close를 isOpen 프로퍼티를 통해 세팅할 수 있다.

 

// MARK: - Command
// 1
public class DoorCommand {
  public let door: Door
  public init(_ door: Door) {
    self.door = door
  }
  public func execute() { }
}

// 2
public class OpenCommand: DoorCommand {
  public override func execute() {
    print("opening the door...")
    door.isOpen = true
  }
}

// 3
public class CloseCommand: DoorCommand {
  public override func execute() {
    print("closing the door...")
    door.isOpen = false
  }
}

Uhhh..?

코드만 봐도 이해가 가지 않는가?

DoorCommand가 있고 이를 상속받아서 excute를 구현한다.

 

// MARK: - Invoker
// 1
public class Doorman {

  // 2
  public let commands: [DoorCommand]
  public let door: Door

  // 3
  public init(door: Door) {
    let commandCount = arc4random_uniform(10) + 1
    self.commands = (0 ..< commandCount).map { index in
      return index % 2 == 0 ?
        OpenCommand(door) : CloseCommand(door)
    }
    self.door = door
  }

  // 4
  public func execute() {
    print("Doorman is...")
    commands.forEach { $0.execute() }
  }
}

Ohhh??

 

2. 변수 선언

commands: [DoorCommand] 선언

door: Door 선언

 

3. 이니셜라이저 부분 

난수를 생성하고, 짝수이면 문을 열고, 홀수이면 문을 닫습니다.

 

// MARK: - Example
public let isOpen = true
print("You predict the door will be " +
  "\(isOpen ? "open" : "closed").")
print("")

 

콘솔에 출력을 해보면 아래와 같은 결과가 나타납니다.

You predict the door will be open.

 

아래와 같은 예제를 볼까요?

let door = Door()
let doorman = Doorman(door: door)
doorman.execute()
print("")

door와 doorman을 만들었다. 그리고 doorman.excute()를 호출한다. 

 

Doorman is...
opening the door...
closing the door...
opening the door...

 

여기서 나타나는 열고 닫기의 결과는 난수에 따라 달라집니다.

 

What should you be careful about?

커맨트 패턴은 많은 커맨드 객체를 만들 수 있습니다. 결과적으로 보면 읽기 및 유지 관리가 더 어려운 코드를 생산할 수도 있습니다. 만약 해당 코드가 나중에 작업을 수행할 필요가 없다면 receiver의 메소드를 직접 호출하는 것이 더 도움 됩니다.

 

Key points

 

이번 챕터에서는 커맨드 패턴에 대해서 학습했다!!

 - 커맨드 패턴은 객체에 작업을 수행하기 위한 정보를 캡슐화한다.

 - invoker(호출자)는 커맨드(명령)을 저장하고 실행한다. command는 작업을 객체로 캡슐화 한다. 마지막으로 recevier는 행동을 직접 취하는 객체이다.

 - 이 패턴은 나중에 저장하고 실행해야 하는 자업에 가장 적합하다. 항상 즉시 작업을 실행하려는 경우에는 receiver에서 직접 메소드를 호출하는 것이 더 나을 수 있다.