この記事シリーズは、iOS/Swiftエンジニアである執筆者個人が、
ごく普通のiOSアプリ開発でよくある状況や
Swiftのコアライブラリやフレームワークで使われているパターンに
着目してデザインパターンを学び直してみた記録です。
関連記事一覧
[iOS/Swift] アプリ開発の実務的アプローチで学ぶデザインパターン
Abstract Factoryパターン概要
- オブジェクトの生成を抽象化することにより、関連/依存する複数のオブジェクト生成を一括して提供するためのパターンです。
- Factory Methodと類似していますが、主な相違点は上記の太字部分です。
- オブジェクト生成の責務を担う側を「抽象的な工場」に見立てて、Abstract Factoryと呼びます。
- 利用側は「工場」に対して生成条件を提示するわけですが、Swiftの場合はenumを上手く使うと良さそうです。
- GoFのデザインパターンでは生成に関するパターンに分類されます。
使い所
- テスト時はDBの処理をモック化するとか
- (iOSアプリではあまりないと思いますが)マルチデータベース対応 とか
サンプルコード
Swiftバージョンは 5.1 です。
// Protocols
protocol DatabaseConnection {
var connection: String { get }
}
protocol DatabaseAccessible {
func select()
func insert()
func delete()
func update()
}
protocol DatabaseProvider {
func makeConnection() -> DatabaseConnection
func makeAccessor() -> DatabaseAccessible
}
// Class
final class MockConnection: DatabaseConnection {
let connection = "MockConnection"
}
final class MockAccessor: DatabaseAccessible {
func select() { print("ダミーを返す") }
func insert() { print("何もしない") }
func delete() { print("何もしない") }
func update() { print("何もしない") }
}
final class ProductionConnection: DatabaseConnection {
let connection = "ProductionConnection"
}
final class ProductionAccessor: DatabaseAccessible {
func select() { print("実際に読み込む") }
func insert() { print("実際に追加する") }
func delete() { print("実際に削除する") }
func update() { print("実際に更新する") }
}
// Abstract factory
enum DatabaseFactoryType: DatabaseProvider {
case mock
case production
func makeConnection() -> DatabaseConnection {
switch self {
case .mock:
return MockConnection()
case .production:
return ProductionConnection()
}
}
func makeAccessor() -> DatabaseAccessible {
switch self {
case .mock:
return MockAccessor()
case .production:
return ProductionAccessor()
}
}
}
// Usage
let mockFactory = DatabaseFactoryType.mock
let mockConnection = mockFactory.makeConnection()
print(mockConnection.connection) // "MockConnection"
let mockAccessor = mockFactory.makeAccessor()
mockAccessor.select() // "ダミーを返す"
let productionFactory = DatabaseFactoryType.production
let productionConnection = productionFactory.makeConnection()
print(productionConnection.connection) // "ProductionConnection"
let productionAccessor = productionFactory.makeAccessor()
productionAccessor.select() // "実際に読み込む"