SwiftのprotocolとextensionをC++のinterface感覚から整理する
Swiftで設計を見ていると、protocol と extension がよく出てきます。
C++の抽象クラスやinterface的なものに近いですが、extensionで後から実装を足せるところがSwiftらしいです。
公式では、protocolは特定の役割に必要なメソッドやプロパティのblueprintを定義するものとして説明されています。
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/protocols/
protocolは要求を書く
protocol Drawable {
func draw()
}
このprotocolに準拠する型は draw() を実装します。
struct Circle: Drawable {
func draw() {
print("circle")
}
}
C++の抽象基底クラスに近いです。
struct Drawable {
virtual void draw() = 0;
virtual ~Drawable() = default;
};
ただしSwiftのprotocolは、classだけでなくstructやenumにも使えます。
extensionで実装を足す
Swiftではextensionで型に機能を追加できます。
extension String {
var isNotEmpty: Bool {
!isEmpty
}
}
既存の型にも追加できます。
if "abc".isNotEmpty {
print("ok")
}
C++なら自由関数にしそうな補助処理が、Swiftではextensionとして書かれることがあります。
protocol extensionでデフォルト実装を書く
protocolに対してextensionを書くと、デフォルト実装を提供できます。
protocol Loggable {
var name: String { get }
}
extension Loggable {
func log() {
print("[log] \(name)")
}
}
準拠する型は name だけ持てば、log() を使えます。
struct User: Loggable {
let name: String
}
User(name: "A").log()
この感じは、C++のCRTPやテンプレート関数で共通処理を足す感覚に少し近いです。
継承の代わりにprotocolを使う場面
Swiftでは、継承よりprotocolで能力を表す場面が多いです。
protocol IdentifiableItem {
var id: String { get }
}
protocol DisplayNameProvider {
var displayName: String { get }
}
必要な能力を小さく切ると、structにもclassにも使えます。
大きな基底クラスを作るより、Swiftではこの形が読みやすいことが多いです。
まとめ
Swiftのprotocolは、C++のinterfaceに近いですが、structやenumにも使えるのが大きいです。
- protocolは要求を書く
- extensionで既存型に機能を足せる
- protocol extensionでデフォルト実装を書ける
- 継承より能力の組み合わせとして使う場面が多い
C++の抽象クラスだけを想像すると少し狭いです。Swiftでは「この型が何をできるか」をprotocolで表す、と見ると理解しやすいです。


