あるメソッドの実行の前にそれ以外のメソッドを必ず実行したい場合にPhantom Typeを利用して実装することができます。例えばBuilderパターンでビルドを行う前に必ず値を設定するメソッドを実行しておきたいといった時です。
以下のコードではageとjobを設定しなければbuildメソッドが呼べません。
class BuilderState {}
class Buildable: BuilderState {}
class Unbuildable: BuilderState {}
class UserBuilder<Age: BuilderState, Job: BuilderState> {
fileprivate let age: UInt
fileprivate let job: String
fileprivate init(age: UInt, job: String) {
self.age = age
self.job = job
}
static var builder: UserBuilder<Unbuildable, Unbuildable> {
return UserBuilder<Unbuildable, Unbuildable>(age: 0, job: "")
}
func set(age: UInt) -> UserBuilder<Buildable, Job> {
return UserBuilder<Buildable, Job>(age: age, job: job)
}
func set(job: String) -> UserBuilder<Age, Buildable> {
return UserBuilder<Age, Buildable>(age: age , job: job)
}
}
extension UserBuilder where Age: Buildable, Job: Buildable {
func build() -> User {
return User(age: age, job: job)
}
}
struct User {
let age: UInt
let job: String
}
let user = UserBuilder.builder.set(age: 39).set(job: "Programmer").build()
BuildStateクラスを基底とするBuildable・UnbuildableクラスはBuilderの状態を表します。Userを作成するためのUserBuilderはAgeとJobという状態を持っています。UserBuilder.builder()でインスタンスを取得するとそのインスタンスはAge・JobともUnbuildableなのでbuildメソッドは実行できません。set(age:)を実行するとAgeはBuildableになります。しかしJobは依然Unbuildableなのでまだbuild()メソッドを実行することはできません。さらにset(job:)メソッドを実行するとAge・JobともBuildableになりbuild()メソッドを実行することができるようになります。
以上のように、Phantom Typeを使うとメソッドの事前実行を強制することが可能です。