使用 Swift 枚举减少函数中的参数数量
Using Swift enum to reduce number of parameters in a function
我正面临函数中参数过多的典型问题。
protocol OfflineController {
func cache(request: OfflineRequestConvertible, forId id: String?, data: Data, keepAliveUntil keepAlive: Date?, completion: @escaping OfflineControllerCompletionHandler)
func get(request: OfflineRequestConvertible, forId id: String?, ifBefore before: Date?, completion: @escaping OfflineControllerCompletionHandler)
func delete(request: OfflineRequestConvertible, forId id: String?, completion: @escaping OfflineControllerCompletionHandler)
}
如您所见,它是一个常规缓存系统,具有 cache
、get
缓存数据和 delete
缓存数据的功能。
此问题的已知解决方案是:
- 查看 SRP 违规情况。我不认为这里是这种情况,至少我没有看到。
- 尝试将相关数据封装在新类型中并传递一个实例。这在经典示例
foo(x: Double, y: Double)
被转换为 foo(point: Point)
中很明显,但在这种情况下,我无法识别任何可以封装的内容(可能是 request
和 id
),并且它'将是类型的爆炸式增长,因为每种方法都有不同的签名。此外,它将复杂性转化为 API 的使用者,必须为每个方法实例化一个具体对象。
- 创建通用参数类型并根据调用的方法使用构建器填充它。恕我直言,它掩盖了 API,我怎么知道我必须在执行
cache
时发送 keepAliveUntil
而不是 get
或 delete
?
我一直在延迟解决这个问题,直到我想起 Swift 的 enum
。现在我正在考虑这样的事情:
enum OfflineControllerAction {
case cache(request: OfflineRequestConvertible, data: Data, id: String?, keepAliveUntil: Date?)
case get(request: OfflineRequestConvertible, id: String?, ifBefore: Date?)
case delete(request: OfflineRequestConvertible, id: String?)
}
protocol OfflineController {
func execute(_ action: OfflineControllerAction, completion: @escaping OfflineControllerCompletionHandler)
}
也许它更优雅,但我认为这与第 2 点非常相似,我保留了在枚举开关中调度的原始方法。
问题是,您如何看待这个解决方案?这是我不知道的另一种方法吗?也许没有解决方案,这只是设计问题 (SRP)?
Step Builder 模式可能是一种根据您的需要逐步添加参数的方法。它还允许您定义在某些情况下是必需的但在其他情况下不是必需的参数(如您在第 3 点中提到的那样)。
简化您必须为每个参数定义一个协议 setter 构建器将具有。接下来是定义一个协议链,因此每个协议都定义了 builder setter 方法,它 returns 是序列中的下一个协议。最后,建造者必须实施所有协议。
使用此模式,您可以定义构建器方法的特定调用序列,从而让您在调用序列中创建备用路径。
我正面临函数中参数过多的典型问题。
protocol OfflineController {
func cache(request: OfflineRequestConvertible, forId id: String?, data: Data, keepAliveUntil keepAlive: Date?, completion: @escaping OfflineControllerCompletionHandler)
func get(request: OfflineRequestConvertible, forId id: String?, ifBefore before: Date?, completion: @escaping OfflineControllerCompletionHandler)
func delete(request: OfflineRequestConvertible, forId id: String?, completion: @escaping OfflineControllerCompletionHandler)
}
如您所见,它是一个常规缓存系统,具有 cache
、get
缓存数据和 delete
缓存数据的功能。
此问题的已知解决方案是:
- 查看 SRP 违规情况。我不认为这里是这种情况,至少我没有看到。
- 尝试将相关数据封装在新类型中并传递一个实例。这在经典示例
foo(x: Double, y: Double)
被转换为foo(point: Point)
中很明显,但在这种情况下,我无法识别任何可以封装的内容(可能是request
和id
),并且它'将是类型的爆炸式增长,因为每种方法都有不同的签名。此外,它将复杂性转化为 API 的使用者,必须为每个方法实例化一个具体对象。 - 创建通用参数类型并根据调用的方法使用构建器填充它。恕我直言,它掩盖了 API,我怎么知道我必须在执行
cache
时发送keepAliveUntil
而不是get
或delete
?
我一直在延迟解决这个问题,直到我想起 Swift 的 enum
。现在我正在考虑这样的事情:
enum OfflineControllerAction {
case cache(request: OfflineRequestConvertible, data: Data, id: String?, keepAliveUntil: Date?)
case get(request: OfflineRequestConvertible, id: String?, ifBefore: Date?)
case delete(request: OfflineRequestConvertible, id: String?)
}
protocol OfflineController {
func execute(_ action: OfflineControllerAction, completion: @escaping OfflineControllerCompletionHandler)
}
也许它更优雅,但我认为这与第 2 点非常相似,我保留了在枚举开关中调度的原始方法。
问题是,您如何看待这个解决方案?这是我不知道的另一种方法吗?也许没有解决方案,这只是设计问题 (SRP)?
Step Builder 模式可能是一种根据您的需要逐步添加参数的方法。它还允许您定义在某些情况下是必需的但在其他情况下不是必需的参数(如您在第 3 点中提到的那样)。
简化您必须为每个参数定义一个协议 setter 构建器将具有。接下来是定义一个协议链,因此每个协议都定义了 builder setter 方法,它 returns 是序列中的下一个协议。最后,建造者必须实施所有协议。
使用此模式,您可以定义构建器方法的特定调用序列,从而让您在调用序列中创建备用路径。