Swift中用无要求协议扩展存通用函数的优劣分析
无约束协议扩展存储通用函数的技术方案优劣分析
我发现一种Swift编码用法:仅通过无约束、无关联类型的协议+协议扩展来存放通用函数,这些函数仅调用全局符号或依赖输入参数,原本完全可以实现为具体类型的静态函数(甚至命名空间式的静态类型)。这类协议除了扩展里提供的默认实现函数外,没有任何协议要求或约束。
示例代码(协议扩展实现)
protocol Naming { func fetchName() -> String } extension Naming { func fetchName() -> String { // 具体实现不影响核心逻辑,仅调用全局对象或依赖参数 // 本质上这段代码完全可以放在某个具体类型的静态方法中 return NotificationCenter.default.description } }
要调用这类函数,必须让目标类型遵循该协议,再通过self调用:
class Road: Naming { func logName() { print(fetchName()) } }
对比常规实现(静态函数/命名空间)
与之相对的常规实现是,把这类函数放在具体类型的静态方法中,甚至可以用一个空结构体作为命名空间:
struct Name { static func fetchName() -> String { return NotificationCenter.default.description } }
对应的调用方式:
class Street { func logName() { print(Name.fetchName()) } }
劣势
你总结的几点劣势非常准确:
- 产生冗余代码:为了获取本可以作为全局/静态函数的功能,需要额外定义协议、让类型遵循协议,增加了不必要的样板代码
- 过度使用协议模式:明明通过组合(直接调用静态函数)就能满足需求,却强制使用协议遵循的模式,违背了"组合优于继承"的设计原则
- 性能损耗:协议方法默认是动态派发,相比静态函数的静态派发会带来一定的性能开销
潜在优势
虽然这种用法看起来弊端明显,但在某些特定场景下可能存在微弱优势:
- 语法便捷性:如果某个类型需要批量接入多个这类通用功能,通过遵循多个协议,可以直接在类型内部用
self.调用所有协议扩展方法,相比每次写Namespace.function()少了一些字符输入 - 后续扩展空间:虽然当前协议没有任何约束,但未来如果需要给这类函数增加类型相关的逻辑,可以直接在协议中添加要求,而不需要修改所有调用点——不过这种场景非常罕见,因为原本的设计就是无约束的通用函数
- 语义分组:相比全局函数,协议可以将功能语义上相关的函数分组,让类型遵循协议相当于"声明该类型具备某一类能力",在代码可读性上有极其微弱的提升(但这种语义提升完全可以通过命名空间实现)
内容的提问来源于stack exchange,提问作者Jeff
相关产品推荐
相关产品推荐

