如何使用具有泛型和多重一致性的子协议来强制在 Swift 框架中保持一致的 API?



我正在尝试使用子协议、多重继承(多重一致性?(和泛型来帮助确保我正在研究的数据收集框架在未来某个时候其他几个开发人员开始贡献时保持一致的 API。

我想记录几种类型的数据,它们都需要时间戳,但我不希望一个通用的"DataSample"被实例化(因此使用协议而不是类/结构(,因为拥有一个不是来自特定数据源的 DataSample 是没有意义的:

protocol DataSample {
var timestamp: Double { get set }
}
struct VideoSample: DataSample {
var timestamp: Double
var width, height: Int
var pixelData: Data
}
struct GyroSample: DataSample {
var timestamp: Double
var x, y, z: Float
}

我正在尝试使用一些协议和子协议来保持所有 appendSample(sample:( 函数尽可能相似(此代码部分可能需要以某种方式更改(:

protocol Recorder {
// This protocol only exists in an attempt to
// make all sub-protocols use a consistent API
func appendSample<T: DataSample>(sample: T)
}
protocol VideoRecorder: Recorder {
func appendSample<VideoSample>(sample: VideoSample)
}
protocol GyroRecorder: Recorder {
func appendSample<GyroSample>(sample: GyroSample)
}

我想允许可以从多个数据源记录的自定义记录器实现。例如,也许我想将视频数据写入 MOV 中的视频轨道,将陀螺仪数据写入同一 MOV 中的元数据轨道:

class CustomRecorder: VideoRecorder, GyroRecorder {
func appendSample<VideoSample>(sample: VideoSample) {
// I want all recorder implementations which conform to
// VideoRecorder to have some sort of function like this.
}
func appendSample<GyroSample>(sample: GyroSample) {
// I want all recorder implementations which conform to
// GyroRecorder to have some sort of function like this.
}
func appendSample<T>(sample: T) where T : DataSample {
// I DON'T want this function to exist at all, but if I
// don't have it here, I get a compile error saying:
// "Type 'CombinedRecorder' does not conform to protocol 'Recorder'"
}
}

协议继承不会带来你要找的东西,因为这不需要程序员实现继承协议中声明的相同函数。没有办法强制以给定的方式实现某个协议;协议只能强制具体类型具有某些 API,而不能强制其他协议。从本质上讲,VideoRecorder具有Recorder的功能,并且可以添加所需的任何额外功能,而与Recorder中的内容无关。

请注意:协议中的方法蓝图VideoRecorderGyroRecorder定义了两个函数,其泛型参数与您声明的类型同名。因此,函数的参数不是您声明的类型,因为泛型参数名称优先。这里有一些文档,有关泛型的更多信息。

似乎您尝试执行的操作可以通过关联类型感到满意。例如,您可以定义以下方案(有点涉及(:

// Samples
protocol DataSample {
var timestamp: Double { get set }
}
struct VideoSample: DataSample {
var timestamp: Double
var width, height: Int
var pixelData: Data
}
struct GyroSample: DataSample {
var timestamp: Double
var x, y, z: Float
}
// Recorder
protocol Recorder {
associatedtype Sample: DataSample
var samples: [Sample] { get set }
mutating func appendSample(sample: Sample)
}
extension Recorder {
mutating func appendSample(sample: Sample) { samples.append(sample) }
}
// Custom recorders
class VideoHandler: Recorder {
typealias Sample = VideoSample
var samples: [VideoSample] = []

// Custom append
func appendSample(sample: VideoSample) {

}
}
class GyroHandler: Recorder {
typealias Sample = GyroSample
var samples: [GyroSample] = []

// Default `append(sample:)` given in extension is used
}

// A specific type to handle film feeds
protocol FilmHandler {
// Generic where clauses ensure that the sample types are correct for whatever types handle what is needed
associatedtype VideoRecorder: Recorder where VideoRecorder.Sample == VideoSample
associatedtype GryoRecorder: Recorder where GryoRecorder.Sample == GyroSample

var videoProcessor: VideoRecorder { get }
var gyroProcessor: GryoRecorder { get }
}
// Custom handler
struct VideoFeed {
func videoSample() -> VideoSample! { nil }
}
class MyHandler: FilmHandler {
typealias VideoRecorder = VideoHandler
typealias GryoRecorder = GyroHandler

var videoProcessor: VideoHandler { VideoHandler() }
var gyroProcessor: GyroHandler { GyroHandler() }

func handle(video: VideoFeed) {
// Call a processor to do something
videoProcessor.appendSample(sample: video.videoSample())
}
}

这里唯一的区别是单个类需要定义其他实例来处理视频的不同部分;但这可能会导致具有更具体赋值的更好地定义的类。

希望这有所帮助。

最新更新