假设您有NSManagedObject类层次结构。
class Dog: NSManagedObject {
var name: String?
}
class Person: NSManagedObject {
var name: String?
}
当然,您不希望重复代码。
你声明协议。
protocol Has_Name_Protocol {
var name: String? {get}
}
extension Has_Name_Protocol {
static func predicate(name: String?) -> NSPredicate {
return predicate(names: [name])
}
static func predicate(names: [String?]?) -> NSPredicate {
return NSPredicate(format: "name == %@", argumentArray: names?.flatMap({$0}))
}
}
当然,这还不够,您还想介绍搜索对象的查找方法。
出于这个原因,您应该将协议绑定到类层次结构中适合的最低绑定。
extension Has_Name_Protocol where Self: NSManagedObject {
static func find(name: String?, context: NSManagedObjectContext) -> Self? {
return nil
}
}
并为符合此协议的子类添加扩展。
extension Dog: Has_Name_Protocol {}
extension Person: Has_Name_Protocol {}
这是一条正确的路吗?
您可以添加一个associatedtype
并将其用作类型。为了方便起见,您还需要添加entityType
。
考虑将name
声明为非可选。这样可以避免所有展开和平面映射。并使find
方法throws
和通过核心数据错误
未测试代码
protocol HasNameProtocol {
associatedtype FetchableType: NSManagedObject = Self
static var entityName : String { get }
var name: String {get}
static func predicate(name: String) -> NSPredicate
static func find(name: String, context: NSManagedObjectContext) throws -> [FetchableType]
}
extension HasNameProtocol where FetchableType == Self {
static var entityName : String {
return NSStringFromClass(self).components(separatedBy: ".").last!
}
static func predicate(name: String) -> NSPredicate {
return NSPredicate(format: "name == %@", name)
}
static func find(name: String, context: NSManagedObjectContext) throws -> [FetchableType] {
let request = NSFetchRequest<FetchableType>(entityName: entityName)
let predicate = predicate(name: name)
request.predicate = predicate
return try context.fetch(request)
}
}
当然,您不希望重复代码。
这不是使用协议的理由。协议不仅仅是一袋语法。你首先需要问"name
的这些用法是否具有语义等价性,从而使共享实现具有有价值的意义?"你不应该问"这些类是否包括相同的击键集?"这可能是巧合,也不是合并它们的好理由,因为如果它们在语义上不同,它们可能会以不同的方式发展。
extension Has_Name_Protocol {
static func predicate(name: String?) -> NSPredicate {
return predicate(names: [name])
}
static func predicate(names: [String?]?) -> NSPredicate {
return NSPredicate(format: "name == %@", argumentArray: names?.flatMap({$0}))
}
}
这缺乏协议扩展所需的一致性。为什么当name
是String
类型时通过String?
是自然的?这创造了什么价值:
func predicate(property: String, values: [String]) -> NSPredicate {
return NSPredicate(format: "(property) == %@", argumentArray: values)
}
func namePredicate(names: [String]) -> NSPredicate {
return predicate(property: "name", values: names)
}
相反,这似乎只是在打乱语法,而不是创建一个有价值的抽象层。
static func find(name: String?, context: NSManagedObjectContext) -> Self?
这个签名令人困惑。为什么name
是可选的?当你说所有适用的东西都必须有一个名字(这是协议的全部内容)时,"找到没有名字的东西"意味着什么?
如果你发现find
的许多实现在语法和语义上都是相同的,那么这里的基本概念可以作为一种语法便利,但我强烈询问这些东西是否比"它们有一个名字"有更深的相似性。如果这是它们唯一重要的相似性,那么协议应该被称为Named
,而不是Has_Name_Protocol
。