我一直在努力解决一个问题,我很确定这个问题会导致我采用Type-Erasure技术,但我不是100%确定。我尝试了几次,感觉很接近,但最终都失败了。我会试着简化我的问题。假设你有一个实体
struct Expense {
var id: Int?
var amount: Double = 0
}和一个等价的领域对象
class RealmExpense: Object {
let id = RealmOptional<Int>()
let amount = RealmOptional<Double>()
var entity: Expense {
return Expense(id: id.value, amount: amount.value)
}
}注意,我可以使用entity变量将RealmExpense转换为Expense。
然后,我有了另一个协议
protocol ExpenseRepository: class {
func getAll() -> [Expense]
//...other CRUD methods
}最后,还有一个类
class ExpenseRealmRepository: ExpenseRepository {
private let realm = try! Realm()
func getAll() -> [Expense] {
return realm.objects(RealmExpense.self).flatMap { $0.entity }
}
func insert(item: Expense) {
try! realm.write {
realm.add(RealmExpense(expense: item))
}
}
//...implementation of other CRUD methods
}现在,这工作得很好,但是我有很多实体,这是我想重构的重复性代码,但我每次尝试创建这个泛型都会抛出这样或那样的编译器错误。我想要的是基本上能够创建一个类
class RealmRepository<RepositoryType: Object, EntityType> {
private let realm = try! Realm()
func getAll() -> [EntityType] {
return realm.objects(RepositoryType.self).flatMap { $0.entity }
}
func insert(item: EntityType) {
try! realm.write {
realm.add(RepositoryType(item))
}
}
//...the rest of the CRUD methods.
}我的主要问题是似乎Swift泛型不允许这种类型的行为。也许有一种更好的整体方法,我还没有探索过。
注: Dave Weston答案的小附录如下:
David Weston在下面给出的答案是正确的(也是很好的),但目前有一个Swift错误,它阻止了领域实体初始化器的使用。请参见:
https://github.com/realm/realm-cocoa/pull/2514
基本上,解决方案是使用接受字典的领域对象的默认初始化器。因为我已经在使用JSON库在实体之间来回转换,所以这就像更改ObjectMapper一样简单
func insert(item: T.EntityType) {
realm.write {
realm.add(RealmEntityType(item)) //compiler error when initializing RealmEntityType
}
}至
func insert(item: T.EntityType) {
let realmItem = RealmEntityType()
realmItem.setValuesForKeys(item.toJSON()) //needs to be a dictionary
try! realm.write {
realm.add(realmItem)
}
}发布于 2017-02-22 06:23:21
这是一个有趣的问题。我不确定您将如何使用这些存储库对象,但根据您到目前为止的问题,我不需要使用类型擦除。
首先,我们定义所需的通用层次结构:
// implemented by your immutable structs
protocol Entity {
}
// Implemented by RealmSwift.Object subclasses
protocol RealmEntity {
associatedtype EntityType
init(_ entity: EntityType)
var entity: EntityType { get }
}
// protocol to define the operations one can perform on a repository
protocol Repository {
associatedtype EntityType
func getAll() -> [EntityType]
func insert(item: EntityType)
}
// A RealmRepository that implements the Repository protocol on top of Realm
class RealmRepository<T>: Repository where T: RealmEntity, T: Object, T.EntityType: Entity {
typealias RealmEntityType = T
private let realm = Realm()
internal func insert(item: T.EntityType) {
realm.write {
realm.add(RealmEntityType(item))
}
}
internal func getAll() -> [T.EntityType] {
return realm.objects(T.self).flatMap { $0.entity }
}
}现在我们有了基础设施,我们可以创建一个ExpenseRepository来显示所有这些东西都是编译的:
struct Expense: Entity {
var id: Int
var amount: Double
}
class RealmExpense: Object, RealmEntity {
typealias EntityType = Expense
let id: RealmOptional<Int>
let amount: RealmOptional<Double>
required init(_ entity: EntityType) {
id = RealmOptional(entity.id)
amount = RealmOptional(entity.amount)
}
var entity: Expense {
return Expense(id: id.value!, amount: amount.value!)
}
}
var expenseRepository = RealmRepository<RealmExpense>()这可能不会在你的项目中编译,因为我是在一个没有导入Realm的游乐场中创建的,但它应该会给你一个想法。
现在,如果您想存储不同类型的存储库的数组,或者是存储库类型的变量,那么就需要进行类型擦除,并且必须创建一个AnyRepository。
发布于 2019-03-20 02:21:58
我正在尝试编写一个库,您可以在以下位置找到它:
https://github.com/rusito-23/RealmDAO
更新:
基本思想是在一个名为GenericDAO的类中处理所有领域事务,该类有两个关联的类型,一个从Object扩展,另一个从Transferrable扩展(这样我们可以处理对象的副本,而不是对象本身)。
还有一个额外的协议和他的方法来处理自动增量主键!
它应该像这样简单地使用:
let movieDAO = GenericDAO<Movie>()
movieDAO.findAll(completion: { /*-- YOUR CODE --*/})你可以在我发送的链接上找到更多信息!
希望它能帮上忙!
https://stackoverflow.com/questions/42369333
复制相似问题