この記事シリーズは、iOS/Swiftエンジニアである執筆者個人が、
ごく普通のiOSアプリ開発でよくある状況や
Swiftのコアライブラリやフレームワークで使われているパターンに
着目してデザインパターンを学び直してみた記録です。
関連記事一覧
[iOS/Swift] アプリ開発の実務的アプローチで学ぶデザインパターン
Commandパターン概要
- Command=命令をオブジェクトとして扱うパターンです。
- Commandオブジェクトは、「パラメータ」と「処理」をカプセル化したモノ。
- GoFのデザインパターンでは振る舞いに関するパターンに分類されます。
使い所
- 並列処理を行いたいケース
- Queueing処理(Commandの待ち行列管理)を行いたいケース
- (DBを使わずに)ある時点の状態へのロールバックを行いたいケース
- Commandオブジェクトの生成元とは異なる場所でCommandを実行したいケース
- 上記1、2を実現するための言語機能であるOperation(とOperationQueue)は、Commandパターンに則って構築されています。
- したがってSwiftでは、Commandパターンのコードを自力で実装しなければならないケースは少なそうです。
- とはいえ前提知識として、Commandパターンのコンセプトは理解しておいて損はないかと思います。
(ご参考)
【Swift】Grand Central Dispatch (GCD)とOperationQueue まとめ > [Operation Queue]
(https://qiita.com/shiz/items/693241f41344a9df6d6f#operation-queue-1)
サンプルコード(Commandパターンの実装例)
Swiftバージョンは 5.1 です。
class Command {
let receiver: Receiver
init(receiver: Receiver) {
self.receiver = receiver
}
func execute() {}
}
class HelloCommand: Command {
override func execute() {
receiver.action(message: "Hello")
}
}
class WorldCommand: Command {
override func execute() {
receiver.action(message: "World")
}
}
protocol Receiver {
func action(message: String)
}
class PrintReceiver: Receiver {
func action(message: String) {
print(message)
}
}
class Invoker {
private var commands = [Command]()
func addCommand(_ command: Command) {
self.commands.append(command)
}
func execute() {
for command in commands {
command.execute()
}
}
}
let invoker = Invoker()
let receiver = PrintReceiver()
let hello = HelloCommand(receiver: receiver)
let world = WorldCommand(receiver: receiver)
invoker.addCommand(hello)
invoker.addCommand(world)
invoker.execute()
// 処理結果
// "Hello"
// "World"
引用元:
しめ鯖日記 - Swiftで学ぶデザインパターン22 (Commandパターン)
サンプルコード(Operationの実装例)
import Foundation
class MyCommand: Operation {
var commandName: String
var delay: Int
init(commandName: String, delay: Int) {
self.commandName = commandName
self.delay = delay
}
override func main() {
for _ in 0..<delay {
sleep(1)
}
print(commandName)
}
}
let operationQueue = OperationQueue()
// 最大同時実行数
operationQueue.maxConcurrentOperationCount = 3
let command1 = MyCommand(commandName: "command1", delay: 1)
let command2 = MyCommand(commandName: "command2", delay: 3)
let command3 = MyCommand(commandName: "command3", delay: 2)
operationQueue.addOperation(command1)
operationQueue.addOperation(command2)
operationQueue.addOperation(command3)
// 処理結果
// "command1"
// "command3"
// "command2"
OperationとOperationQueueを使ってみると、Commandパターンになっていることが理解できるかと思います。