编写自定义扩展时的代码重复问题:如何优化重复的Range扩展代码
解决CountableRange和CountableClosedRange代码重复的方案
这种重复实现确实挺烦人的,完全可以通过自定义通用协议的方式彻底消除重复,只写一次逻辑就能覆盖两种范围类型。下面是具体的实现步骤:
1. 定义通用的BasicRange协议
我们先创建一个协议,把两个range类型共有的必要特性抽象出来——包括访问上下界、通过uncheckedBounds初始化,以及约束Bound必须是Strideable类型(这样才能调用advanced(by:)):
protocol BasicRange: RangeExpression where Bound: Strideable { var lowerBound: Bound { get } var upperBound: Bound { get } init(uncheckedBounds bounds: (lower: Bound, upper: Bound)) }
2. 让系统类型遵循协议
接下来只需要让CountableRange和CountableClosedRange遵循这个协议就行,因为它们本身就满足所有协议要求,不需要额外实现任何内容:
extension CountableRange: BasicRange {} extension CountableClosedRange: BasicRange {}
3. 为协议扩展实现通用逻辑
现在我们只需要给BasicRange写一次扩展,就能让所有遵循它的类型(也就是我们的两个range类型)自动获得对应的方法和运算符:
public extension BasicRange { func advanced(by n: Bound.Stride) -> Self { let newLower = lowerBound.advanced(by: n) let newUpper = upperBound.advanced(by: n) return Self(uncheckedBounds: (newLower, newUpper)) } static func + (lhs: Self, rhs: Bound.Stride) -> Self { lhs.advanced(by: rhs) } static func - (lhs: Self, rhs: Bound.Stride) -> Self { lhs.advanced(by: -rhs) } }
为什么这个方案可行?
虽然CountableRange和CountableClosedRange在是否包含上界这一点上行为不同,但在整体平移范围这个场景下,它们的逻辑完全一致——只需要同步移动上下界即可。我们的协议刚好抽象了这个场景需要的所有能力,所以通用扩展完全适用。
这样改造之后,你就不用再维护两段几乎一样的代码了,后续如果要修改逻辑,只需要改协议扩展里的实现就行~
内容的提问来源于stack exchange,提问作者Roman
相关产品推荐
相关产品推荐

