我认为这里的类型关系相当简单,但我错过了错误的原因.错误是:“类型’T’不符合协议’EntityType’”(在ThingManager处):
//
protocol EntityType {
typealias Identifier
var identifier : Identifier { get }
}
class EntityWithStringIdentifier : EntityType {
var identifier : String
init (i:String) { self.identifier = i }
}
class Thing : EntityWithStringIdentifier {}
//
protocol EntityManager {
typealias Entity : EntityType
func has (entity:Entity) -> Bool
}
class BaseEntityManager<Entity:EntityType> : EntityManager {
func has (entity:Entity) -> Bool { return true }
}
// Type 'T' does not conform to protocol 'EntityType'
class ThingManager<T:Thing> : BaseEntityManager<T> {
}
T是Thing的子类型; Thing是实现EntityType的EntityWithStringIdentifier的子类型.那么,为什么错误呢?
显然,使用以下方法可以避免错误:
class ThingManager<T:Thing where T:EntityType> : BaseEntityManager<T> {
}
然后可以用Thing实例化ThingManager(暗示Thing首先实现了EntityType ……)
var tm = ThingManager<Thing>()
从类型的角度来看,有没有更好的方法来实现这种DAO模式?
最佳答案 随着Swift 2.0的发布,以早期版本的Swift来回答这个问题似乎毫无意义,很快就会过时了.我可以确认这个问题 – 我认为是一个bug – 仍然存在于Swift 2.0中.但是,我可以建议一种稍微不同的方式来组织代码,以缓解问题,同时仍然有一个干净的实现:跳过ThingManager类并使用协议扩展.
因此,将所有代码保留为BaseEntityManager,您将拥有:
// NOTE "where Entity: Thing". This is the key.
extension EntityManager where Entity: Thing {
func useAThing(thing: Thing) {
}
}
let tm = BaseEntityManager<Thing>()
tm.useAThing(Thing(i: "thing"))
我怀疑这最终会成为做事的“快捷方式”. Swift的泛型对我来说简直很奇怪.他们经常违反最低惊喜原则,恕我直言.但如果你了解他们的怪癖,最终他们会非常强大.这种方法的唯一缺点是访问修饰符和封装. useAThing方法无法访问EntityManagerBase的私有状态,根据您的具体情况,这可能是也可能不是问题.
最后,如果你采用这种方法,我建议一些重命名.如果您从未将其子类化,BaseEntityManager感觉就像是错误的名称.相反,我将其称为EntityManager并将协议重命名为EntityManagerType以遵循Apple的约定.