[Swift] 異なるProtocolが同名のメソッドを持ち単一のクラスが両方のプロトコルに準拠したい場合
はじめに
この記事ではSwiftで複数のprotocolが同名のメソッドを持つ場合にどのようにすれば良いかのメモです。この記事では_
から始まる属性を使用しているので実プロダクトでは使用しない方が良いと思います。知見として面白いのでメモにしました。
TL;DR
@_implements
を使うことで同名のメソッドを持つprotocolを単一のクラスで実装することができます。
コード例
例えばAというプロトコルがconfigure
というメソッドを宣言しているとします。また他にもBというプロトコルがconfigure
というメソッドを宣言しているとします。
protocol A {
func configure()
}
protocol B {
func configure()
}
上記のA
とB
というプロトコルの両方にあるクラスC
が準拠する必要がある場合、メソッド名が衝突して困ります。
protocol A {
func configure()
}
protocol B {
func configure()
}
class C: A, B {
func configure() {
// コンパイルが通るが、AとBの実装を分けることができない
print("cofigured!")
}
}
解決策
@_implements
属性を用いることでクラスが実装するメソッドのシグネチャとprotocolのシグネチャを分けることができます。
上記のAとBの実装をC側で別々にしたい場合は以下のように書くことができます。
@_implements("プロトコル名", "メソッドのシグネチャ")
と書けば良いです。(例: @_implements(B, configure())
)
protocol A {
func configure()
}
protocol B {
func configure()
}
class C: A, B {
@_implements(A, configure())
func configureA() {
print("A is cofigured!")
}
@_implements(B, configure())
func configureB() {
print("B is configured!")
}
}
ケーススタディ
あまり実用的な例はありませんが、考え方としては同名の関数が複数のプロトコルで定義されている場合に有効です。
例えばIDを生成する責務を持つクラスがあったとして、その実装を要求するプロトコルにRandomIDGenerator
とIncrementalIDGenrator
があったとします。RandomIDGeneratorは順番に関係なくランダムにIDを生成しますが、IncrementalIDGeneratorは数字を1つずつインクリメントしてIDを生成します。
protocol IncrementalIDGenerator {
func make() -> String
}
protocol RandomIDGenerator {
func make() -> String
}
class IDGenerator: IncrementalIDGenerator, RandomIDGenerator {
private var count: Int = 0
@_implements(IncrementalIDGenerator, make())
func makeIncremental() -> String {
count += 1
let formatter = NumberFormatter()
formatter.minimumIntegerDigits = 32
return formatter.string(for: count)!
}
@_implements(RandomIDGenerator, make())
func makeRandom() -> String {
count += 1
return (0..<32)
.map { _ in Int.random(in: 0...9) }
.map { String($0) }
.joined(separator: "")
}
}
大抵の場合はmakeRandom
とmakeIncremental
をプロトコルで分けるのではなく実体(class)で分けることが多いと思いますが、実体を同じにすることでcount
という変数を共有できました。(他にもやり方はあると思います)
RandomIDGenerator | IncrementalIDGenerator |
---|---|
注意
下のURLにあるように_
から始まる属性は使うことを非推奨されているので使わない方が良いと思いますが、知見としてメモをするべく記事にしました。
Discussion