我想实现一个系统,它允许我通过为不同的键路径注册处理器来处理一个类型。
系统的一个特点应该是composition,因此每个处理器都应该扩展一个通用的通用协议Processor。
用法示例:
struct Language {
var name = "Swift"
var version = 5.3
}
var processor = TypeProcessor<Language>()
processor.add(procesor: VersionProcessor(), keypath: \.version)
processor.add(procesor: NameProcessor(), keypath: \.name)
var input = Language()
processor.process(value: input)
// Languge version: 5.3
// Languge name: Swift发布于 2021-01-30 19:59:19
我已经创建了一个playground,展示了如何使用函数组合来解决这个问题,然后使用我们在那里学到的知识重新创建了您的示例。
只要类型匹配,函数组合就允许您通过将现有函数链接在一起来创建新函数。
precedencegroup CompositionPrecedence {
associativity: left
}
infix operator >>>: CompositionPrecedence
func >>> <T, U, V>(lhs: @escaping (T) -> U, rhs: @escaping (U) -> V) -> (T) -> V {
return { rhs(lhs($0)) }
}可以将Processor转换为一个函数,该函数接受一个对象O,以某种方式转换它,然后返回一个新对象。创建函数可以像这样完成:
func process<O, K>(keyPath: WritableKeyPath<O, K>, _ f: @escaping (K) -> K) -> (O) -> O {
return { object in
var writable = object
writable[keyPath: keyPath] = f(object[keyPath: keyPath])
return writable
}
}
let reverseName = process(keyPath: \Person.name, reverse)
let halfAge = process(keyPath: \Person.age, half)现在我们可以组合这两个函数了。得到的函数仍然保留签名‘( Person) -> Person)。我们可以根据需要组合任意多个函数,从而创建一个处理管道。
let benjaminButton = reverseName >>> halfAge
let youngBradPitt = benjaminButton(bradPitt)接下来重新创建您的示例。正如这个answer所提到的,该类型是根对象上的泛型。这就像在函数组合示例中一样,它允许我们对数组中的所有处理器进行分组。
protocol Processor {
associatedtype T
func process(object: T) -> T
}在擦除对象时,重要的是保留对原始对象的引用,这样我们就可以使用它来实现所需的功能。在本例中,我们保留了对其process(:)方法的引用。
extension Processor {
func erased()-> AnyProcessor<T> {
AnyProcessor(base: self)
}
}
struct AnyProcessor<T>: Processor {
private var _process: (T) -> T
init<Base: Processor>(base: Base) where Base.T == T {
_process = base.process
}
func process(object: T) -> T {
_process(object)
}
}这里有两种实现Processor协议的类型。请注意,第一个有两种占位符类型。第二个占位符将被擦除。
struct AgeMultiplier<T, K: Numeric>: Processor {
let multiplier: K
let keyPath: WritableKeyPath<T, K>
private func f(_ value: K) -> K {
value * multiplier
}
func process(object: T) -> T {
var writable = object
writable[keyPath: keyPath] = f(object[keyPath: keyPath])
return writable
}
}
struct NameUppercaser<T>: Processor {
let keyPath: WritableKeyPath<T, String>
private func f(_ value: String) -> String {
value.uppercased()
}
func process(object: T) -> T {
var writable = object
writable[keyPath: keyPath] = f(object[keyPath: keyPath])
return writable
}
}最后,使用对象组合的ObjectProcessor。请注意,该数组包含相同类型的对象。例如,此结构的实例将只能处理Person。每个子处理器所做的事情都隐藏在实现中,并且它可以在不同类型的数据上运行这一事实不会影响ObjectProcessor。
struct ObjectProcessor<T>: Processor {
private var processers = [AnyProcessor<T>]()
mutating func add(processor: AnyProcessor<T>) {
processers.append(processor)
}
func process(object: T) -> T {
var object = object
for processor in processers {
object = processor.process(object: object)
}
return object
}
}这就是它的作用。请注意,我为同一个键添加了两个处理器。
var holyGrail = ObjectProcessor<Person>()
holyGrail.add(processor: NameUppercaser(keyPath: \Person.name).erased())
holyGrail.add(processor: AgeMultiplier(multiplier: 2, keyPath: \Person.age).erased())
holyGrail.add(processor: AgeMultiplier(multiplier: 3, keyPath: \Person.age).erased())
let bradPitt = Person(name: "Brad Pitt", age: 57)
let immortalBradPitt = holyGrail.process(object: bradPitt)发布于 2021-01-30 18:47:29
您可以使用“处理器”的继承,而不是尝试擦除属性类型,其中子类是根对象和属性类型的泛型,并具有属性键路径。
protocol Processor {
associatedtype T
func process(value: T)
}
class AnyProcessor<Value>: Processor {
func process(value: Value) {
}
}
final class KeyPathProcessor<Root, P: Processor>: AnyProcessor<Root> {
typealias Value = P.T
let keyPath: KeyPath<Root, Value>
let processor: P
init(keyPath: KeyPath<Root, Value>, processor: P) {
self.keyPath = keyPath
self.processor = processor
}
override func process(value: Root) {
processor.process(value: value[keyPath: keyPath])
}
}
struct ObjectProcessor<T>: Processor {
var processors = [AnyKeyPath: AnyProcessor<T>]()
mutating func add<P: Processor, V>(processor: P, keypath: KeyPath<T, V>) where P.T == V {
self.processors[keypath] = KeyPathProcessor(keyPath: keypath, processor: processor)
}
func process(value: T) {
for (_, processor) in processors {
processor.process(value: value)
}
}
}发布于 2021-02-01 14:55:48
感谢您的回答。我已经使用了您的输入,并提出了以下解决方案:
protocol Processor {
associatedtype T
func process(value: T)
}
extension Processor {
func erase()-> AnyProcessor<T> {
return AnyProcessor<T>(self)
}
}
struct AnyProcessor<T>: Processor {
let process: (T) -> Void
init<P: Processor>(_ processor: P) where P.T == T {
self.process = processor.process
}
func process(value: T) {
self.process(value)
}
}
struct KeyPathProcessor<T, V>: Processor {
private var keyPath: KeyPath<T,V>
private var processor: AnyProcessor<V>
init<P: Processor>(_ processor: P, for keyPath: KeyPath<T, V>) where P.T == V {
self.processor = processor.erase()
self.keyPath = keyPath
}
func process (value: T) {
let input = value[keyPath: keyPath]
processor.process(value: input)
}
}
struct VersionProcessor: Processor {
func process(value: Double) {
print("Languge version: \(value)")
}
}
struct NameProcessor: Processor {
func process(value: String) {
print("Languge name: \(value)")
}
}
struct TypeProcessor<T>: Processor {
var processors = [AnyProcessor<T>]()
mutating func add<P: Processor, V>(procesor: P, keypath: KeyPath<T, V>) where P.T == V {
let p = KeyPathProcessor(procesor, for: keypath).erase()
self.processors.append(p)
}
func process(value: T) {
for processor in processors {
processor.process(value: value)
}
}
}
struct Language {
var name = "Swift"
var version = 5.3
}
var processor = TypeProcessor<Language>()
processor.add(procesor: VersionProcessor(), keypath: \.version)
processor.add(procesor: NameProcessor(), keypath: \.name)
var input = Language()
processor.process(value: input)
// Languge version: 5.3
// Languge name: Swifthttps://stackoverflow.com/questions/65961943
复制相似问题