[iOS 8] Swiftでデザインパターン No.11 Prototype

2015.04.15

Prototype

Prototype とは

英単語 Prototype には以下のような意味があります。

  • 原型
  • 試作品

Prototype パターンは、生成すべきオブジェクトの種類を原型となるインスタンスを使って明確にし、それをコピーすることで新たなオブジェクトの生成を行うパターンです。

クラス図

prototype

ポイント

  • clone メソッドは、自身の複製を返す

サンプルケース

ジェダイ・マスターのサイフォ=ディアスは、銀河共和国の兵力増強のため、120万人のトルーパー(兵士)を集める必要がある。 一から兵を集め訓練を施すのでは間に合わないため、卓越したクローン技術を持つカミーノアン(惑星カミーノの住人)に優秀な兵士のクローンを製造してもらうことにした。

これをプログラムで表現します。

サンプルコード

Kaminoan

protocol Kaminoan {
func clone() -> AnyObject
}

Prototype を表すプロトコルです。 clone メソッドのインタフェースを提供します。

Trooper

class Trooper: NSObject, Kaminoan {

var power: Int?
var speed: Int?

func clone() -> AnyObject {
let cloneTrooper = Trooper()
cloneTrooper.power = self.power
cloneTrooper.speed = self.speed

return cloneTrooper
}
}

ConcretePrototype クラスです。兵士を表します。 パワー・スピードというプロパティを持ち、clone メソッドを実装します。

Jedi

class Jedi: NSObject {

func createManyTroopers() -> [Trooper] {
var troopers = [Trooper]()
let jango = Trooper()

// トルーパー(オリジナル)に訓練を行う
self.train(jango)

// 120万人のトルーパーを生成
for _ in 0..<1200000 {
let newTrooper = jango.clone() as! Trooper
troopers.append(newTrooper)
}

return troopers
}

private func train(trooper: Trooper) {
// トルーパーを訓練する
// とても時間がかかる処理
trooper.power = 100
trooper.speed = 100
}
}

Client クラスです。ジェダイを表します。 訓練は一人にのみ行い、残りのトルーパーはその兵士のクローンとして用意します。

結果

兵力増強が間に合い、独立星系連合のドロイド軍を見事に撃退することができました。

Happy End.

このように Prototype パターンはオブジェクトの生成コストが高い時の回避策として利用されることがあります。

for in 時の_(アンダースコア)

以下の for 文に注目してください。

for _ in 0..<1200000 {
let newTrooper = jango.clone() as! Trooper
troopers.append(newTrooper)
}

ここでは単に120万回ループを繰り返しているだけで、ループ変数は使用していません。 その場合は for の後に_を付けることによってループ変数を無視することができます。

まとめ

  • for 文でループ変数を使用しない時は_を付ける

おまけ

Swift(& Objective-C)では、インスタンスのコピーを返すcopyというメソッドが NSObject に定義されています。 このメソッドを利用するには NSCopying プロトコルを採用する必要がありますが、これを使うと自前で clone メソッドを用意する必要はなくなります。

class Trooper: NSObject, NSCopying {

var power: Int?
var speed: Int?

func copyWithZone(zone: NSZone) -> AnyObject {
let cloneTrooper = Trooper()
cloneTrooper.power = self.power
cloneTrooper.speed = self.speed

return cloneTrooper
}
}
for _ in 0..<1200000 {
let newTrooper = jango.copy() as! Trooper
troopers.append(newTrooper)
}