Swift子类通过绑定到超类来采用自定义协议



假设您有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}))
}
}

这缺乏协议扩展所需的一致性。为什么当nameString类型时通过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

最新更新