這個章節要來實作 Repository 的程式碼。
記得我們在 Clean Architecture 章節,定義了 Repository 這個 Protocol 的內容嗎?
protocol Repository: MemberRepository {}
protocol MemberRepository {
func getMember(_ name: String?, _ position: Position?) throws -> [Member]
func saveMember(_:Member) throws -> Member?
func updateMember(_:Member) throws
func deleteMember(_:Member) throws
}
首先我們先新建一個 Dao.swift 並建立一個 Dao 結構。
這個結構會繼承 Repository 之後實作方法:

不過這樣寫我們可以預期,在未來方法變多了,就會很難管理對吧?
所以我習慣會把他們分開,再讓 Dao 繼承。
但由於這邊我想用 struct 來寫,所以額外的 Dao 分類就要用 Protocol 來實作。
什麼意思?實際寫寫看就知道了。
首先我們在 Repository 資料夾內建立 SQLite 資料夾,在裡面新增 SQLiteDao.swift,並定義 SQLiteDao Protocol:

protocol SQLiteDao {}
接著在同樣 SQLite 資料夾裡新增 SQLiteMemberDao.swift,並定義 SQLiteMemberDao Protocol:
protocol SQLiteMemberDao {}
接下來利用 where Self: {Protocol} 並在 SQLiteMemberDao 內實作方法,這邊我們要實作 MemberRepository:

import Foundation
import Sworm
import SQLite
protocol SQLiteMemberDao {}
extension SQLiteMemberDao where Self: MemberRepository {
func getMember(_ name: String?, _ position: Position?) throws -> [Member] {
let rows = try SQL.getDriver().query(Member.self) { t in
if let name = name, let position = position {
return t.where(Member.name == name && Member.position == position)
}
if let name = name {
return t.where(Member.name == name)
}
if let position = position {
return t.where(Member.position == position)
}
return t
}
var result = [Member]()
for row in rows {
result.append(try Member.parse(row))
}
return result
}
func saveMember(_ member: Member) throws -> Member? {
let id = try SQL.getDriver().insert(member)
return Member(id: id, name: member.name, position: member.position)
}
func updateMember(_ member: Member) throws {
let _ = try SQL.getDriver().update(member, where: Member.id == member.id)
}
func deleteMember(_ member: Member) throws {
let _ = try SQL.getDriver().delete(Member.self) { $0.where(Member.id == member.id) }
}
}
方法都實作完了,接下來就要讓他全部繼承回 Dao 了!
首先先將 SQLiteDao 繼承 SQLiteMemberDao:
protocol SQLiteDao: SQLiteMemberDao {}
接下來再將 Dao 繼承 SQLiteDao:
struct Dao: SQLiteDao {}
接著會發現,就算 Dao 結構沒有實作 Repository 的方法,也不會噴錯:

這是因為,我們已經在 SQLiteMemberDao 內實作方法了。
接下來我們要在 Dao 的 init 方法內,連線到資料庫並建立 Table:

struct Dao: SQLiteDao {
init() {
let conn = SQL.setup(dbName: "database", isMock: false)
conn.migrate([Member.self])
}
}
這樣就大功告成了!
我們使用 Protocol 來實作 Repository 內的方法。
這樣的優點是未來如果不想用 SQLite,想改成別的比方說 MySQL。
不用把外部使用的方法重寫,也不用把 SQLiteDao 整個重寫,只要再新寫一個 MySQLDao 就能達到抽換資料庫的目的,又可以降低對資料庫實作的耦合。