Swiftでデザインパターンを学習 ~Abstract Factory~


定義

Abstract Factory パターンは、関連する一連のインスタンスを状況に応じて、適切に生成する方法を提供する。
https://ja.wikipedia.org/wiki/デザインパターン_(ソフトウェア)

実装してみた

今回は車の部品製造に関するAbstract Factoryを実装してました。

classes.swift
// Class
class Car {
    var engine: Engine?
    var body: Body?
    var headLight: HeadLight?
}

// Structs
struct Engine {
    enum Types {
        case reciprocating
        case rotary
        case diesel
        case hybrid
    }

    let type: Types
}

struct Body {
    let color: UIColor
    let mmLength: Int
}

struct HeadLight {
    enum Types {
        case normal
        case retractable // COOL!
    }

    let type: Types
}

Factory

RX-7とRX-8はロータリーエンジンを使い回せていますが、ヘッドライトはリトラクタブルとノーマルライトで違いますし、ボディはそれぞれ専用のものになっています。

Factory.swift
class RXSevenFactory: AbstractCarFactory {

    func makeBody() -> Body {
        return RXSevenBody().create()
    }

    func makeEngine() -> Engine {
        return RotaryEngine().create()
    }

    func makeHeadLight() -> HeadLight {
        return RetractableHeadLight().create()
    }
}

class RXEightFactory: AbstractCarFactory {

    func makeBody() -> Body {
        return RXEightBody().create()
    }

    func makeEngine() -> Engine {
        return RotaryEngine().create()
    }

    func makeHeadLight() -> HeadLight {
        return NormalHeadLight().create()
    }
}

class HachirokuFactory: AbstractCarFactory {

    func makeBody() -> Body {
        return HachirokuBody().create()
    }

    func makeEngine() -> Engine {
        return ReciprocatingEngine().create()
    }

    func makeHeadLight() -> HeadLight {
        return RetractableHeadLight().create()
    }
}

Product

product.swift
// Body
protocol AbstractBody {
    func create() -> Body
}

class RXSevenBody: AbstractBody {

    func create() -> Body {
        return Body(color: .yellow, mmLength: 4285)
    }
}

class RXEightBody: AbstractBody {

    func create() -> Body {
        return Body(color: .red, mmLength: 4470)
    }
}

class HachirokuBody: AbstractBody {

    func create() -> Body {
        return Body(color: .white, mmLength: 4180)
    }
}

// Engine
protocol AbstractEngine {
    func create() -> Engine
}

class RotaryEngine: AbstractEngine {

    func create() -> Engine {
        return Engine(type: .rotary)
    }
}

class ReciprocatingEngine: AbstractEngine {

    func create() -> Engine {
        return Engine(type: .reciprocating)
    }
}

// Head Light
protocol AbstractHeadLight {
    func create() -> HeadLight
}

class NormalHeadLight: AbstractHeadLight {
    func create() -> HeadLight {
        return HeadLight(type: .normal)
    }
}

class RetractableHeadLight: AbstractHeadLight {
    func create() -> HeadLight {
        return HeadLight(type: .retractable)
    }
}

クライアントコード

client.swift
var factory = makeFactory(for: .rxSeven)
var rxSeven = Car()
rxSeven.body = factory.makeBody()
rxSeven.engine = factory.makeEngine()

思ったこと

上記の記事の中ほどが分かりやすかったのですが、
理解としては
複数の関連オブジェクトを、色々なパターンで組み合わせて使うときに使うと便利。AbstractFactoryの変更以外では、オープンクローズドの原則が保たれている。
という感じです。

混乱した点

・他の記事を参考にしていると「1つのConcreteFactoryのメソッドに1つのConcreteProduct」という対応のコードが多かったのですが、1箇所からしか使われないのであればConcreteFactoryのメソッド内に初期化コード直書きでもよさそうに感じたので、1つのConcreteProductを複数のConcreteFactoryから使う形にして、再利用性を意識した形にしてみるとスッキリしました。

・上記の記事(ググったらトップに表示される)の内容ではメソッド内で、以下のようにしていました。

MizutakiFactory.java
public List<Vegetable> getVegetables(){
        List<Vegetable> vegetables = new ArrayList<Vegetable>();
        vegetables.add(new ChineseCabbage());
        vegetables.add(new Leek());
        vegetables.add(new Chrysanthemum());
        return vegetables;
    }

これはVegetableクラスを継承したクラスの初期化処理で特有の値をセットしているのだと思いますが、欲しいクラスを初期化するのがConcreteProductの役割と考えているデザインパターン初学者の僕からすれば、このコードではAbstract Factoryパターンに本当に適合しているのかどうか分かりませんでした。他の記事と形が違うので混乱しました。

サンプルコード

GitHubに置いてます。
https://github.com/syatyo/swift-design-pattern-sample/blob/master/AbstractFactory.playground/Contents.swift