[iOS 8] Swiftでデザインパターン No.10 Builder

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Builder

Builder とは

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

  • 組み立てる
  • 建築する

Builder パターンは、オブジェクトの生成過程を抽象化することによって、動的なオブジェクトの生成を可能にするパターンです。

クラス図

builder

ポイント

  • 抽象クラス Builder の buildPart オペレーションはデフォルトでは何も行わない
  • 各 ConcreteBuilder クラスが、生成する構成要素に対するオペレーションのみをオーバーライドする

サンプルコード

今回は「勇者」オブジェクトを生成し、以下の項目を設定するプログラムを考えてみました。

  • レベル
  • ぶき
  • よろい

Builder

class Builder: NSObject {
    func buildLevel(level: Int) {}
    func buildSword(sword: String) {}
    func buildArmor(armor: String) {}
}

こちらは Builder クラスです。
「レベル」、「ぶき」、「よろい」を設定するメソッドを持っています。

HeroBuilder

class HeroBuilder: Builder {

    let hero = Hero()

    override func buildLevel(level: Int) {
        self.hero.level = level
    }

    override func buildSword(sword: String) {
        self.hero.sword = sword
    }

    override func buildArmor(armor: String) {
        self.hero.armor = armor
    }

    func getResult() -> Hero {
        return self.hero
    }
}

こちらは ConcreteBuilder クラスです。
「勇者」オブジェクトを構築するクラスです。

Hero

class Hero: NSObject {

    var level: Int?
    var sword: String?
    var armor: String?

    func showStatus() {
        println("### Status ###")
        println("Level: (self.level)")
        println("Sword: (self.sword)")
        println("Armor: (self.armor)")
        println("##############")
    }

    func checkStatus() {
        if let level = self.level, sword = self.sword, armor = self.armor where level > 10 {
            println("そなたは もう じゅうぶんにつよい!")
        } else {
            println("レベルが あがったら また くるがいい")
            println("ぶきや ぼうぐは そうびしないと いみがないぞ")
        }
    }
}

こちらは Product クラスです。
「勇者」を表します。
ステータスを表示するメソッド、ステータスをチェックするメソッドを持ちます。

Director

class Director: NSObject {

    let builder: Builder

    init(builder: Builder) {
        self.builder = builder
    }

    func construct() {
        self.builder.buildLevel(30)
        self.builder.buildSword("おうじゃのけん")
        self.builder.buildArmor("ひかりのよろい")
    }
}

こちらは Director クラスです。
Builder クラスのインタフェースを使って「勇者」を生成します。

実行

let heroBuilder = HeroBuilder()
let director = Director(builder: heroBuilder)
director.construct()

let hero = heroBuilder.getResult()
hero.showStatus()
hero.checkStatus()

実行結果

### Status ###
Level: Optional(30)
Sword: Optional("おうじゃのけん")
Armor: Optional("ひかりのよろい")
##############
そなたは もう じゅうぶんにつよい!

オブジェクトを一度に作成するパターンとは違い、Builder パターンでは Product オブジェクトを段階的に作成します。
これにより Product オブジェクトの内部構造を、より細かくコントロールすることができるようになります。

余裕があれば、装備に「たて」や「かぶと」、キャラクターに「戦士」や「魔法使い」が追加された時についても考えてみましょう。

Multiple Optional Bindings

Hero クラスに無理やり作成した以下のメソッドに注目してください。

func checkStatus() {
    if let level = self.level, sword = self.sword, armor = self.armor where level > 10 {
        println("そなたは もう じゅうぶんにつよい!")
    } else {
        println("レベルが あがったら また くるがいい")
        println("ぶきや ぼうぐは そうびしないと いみがないぞ")
    }
}

ここでは Optional Bindings を使用していますが、Swift 1.2 からは複数の変数について一行の if 文で書けるようになりました。
ネストが深くなることがなくなるので、これは嬉しい進化ですね。(右に長くなってはしまいますが・・・)

まとめ

  • Swift 1.2 からは複数の Optional Bindings に対応した!
  • バインドした変数について条件式も同時に書ける!

リンク