如何在Swift中正确使用类扩展?

在Swift中,我一直使用扩展来扩展封闭types,并提供方便的,无逻辑的function,比如animation,math扩展等。但是,由于扩展是遍布整个代码库的硬依赖,所以在实现作为延伸的东西。

不过,最近我已经看到,苹果build议在更大程度上使用扩展,例如将协议作为单独的扩展来实现。

也就是说,如果你有一个实现协议B的类A,那么你最终得到这个devise:

class A { // Initializers, stored properties etc. } extension A: B { // Protocol implementation } 

当你进入这个兔子洞,我开始看到更多基于扩展的代码,例如:

 fileprivate extension A { // Private, calculated properties } fileprivate extension A { // Private functions } 

我的一部分就像你在分开的扩展中实现协议时所获得的构build块。 它使得这个类的不同部分真正的不同。 但是,只要inheritance这个类,就不得不改变这个devise,因为扩展函数不能被覆盖。

我认为第二种方法是…有趣的。 有一件好事就是你不必注释每个私人财产和function作为私人,因为你可以指定的扩展。

然而,这种devise也分裂了存储和非存储的属性,公共和私人function,使得类的“逻辑”更难以遵循(我写小类)。 那和子类问题一起,让我在扩展仙境的门廊上停住了一点。

很想听听Swift社区如何看待扩展。 你怎么看? 有一个silverbullet?

当然,这只是我的看法,所以把我写的容易。

我目前在我的项目中使用extension-approach有几个原因:

  • 代码很干净 :我的类永远不会超过150行,通过扩展进行分隔使我的代码更具可读性,并按职责分开

这通常是一个类的样子:

 final class A { // Here the public and private stored properties } extension A { // Here the public methods and public non-stored properties } fileprivate extension A { // here my private methods } 

扩展可以不止一个,当然,这取决于你的课程。 这对于组织代码并从Xcode顶部栏读取它非常有用

扩展描述

  • 它提醒我Swift是一种面向协议的编程语言,而不是OOP语言。 协议和协议扩展无法做到。 我更喜欢使用协议将安全层添加到我的类/结构中。 例如,我通常以这种方式写我的模型:

     protocol User { var uid: String { get } var name: String { get } } final class UserModel: User { var uid: String var name: String init(uid: String, name: String) { self.uid = uid self.name = name } } 

通过这种方式,您仍然可以在UserModel类中编辑您的uidname值,但不能在外面,因为您只能处理User协议types。

我使用了类似的方法,可以用一句话来描述:

将types的职责分类为扩展

这些是我正在把个别扩展的方面的例子:

  • types的主界面,从客户端看。
  • 协议一致性(即委托协议,通常是私有的)。
  • 序列化(例如与NSCoding相关的所有内容)。
  • 生活在后台线程中的部分types,如networkingcallback。

有时候,当一个方面的复杂性上升时,我甚至会将一个types的实现分成多个文件。

这里有一些细节描述我如何sorting实现相关的代码:

  • 重点是function成员资格。
  • 保持公共和私人的实现closures,但分开。
  • 不要在varfunc之间分割。
  • 保持function实现的所有方面:嵌套types,初始化器,协议一致性等

优点

分离一个types的主要原因是为了更容易阅读和理解。

在阅读外国(或我自己的旧)代码时,理解大局常常是潜水中最困难的部分。给开发人员一个方法上下文的概念帮助很大。

还有另一个好处:访问控制使得不会无意中调用某些东西。 只能从后台线程调用的方法可以在“后台”扩展中声明为private 。 现在根本不能从其他地方调用。

目前的限制

Swift 3对这种风格施加了一定的限制。 有几件事情只能在主types的实现中生存:

  • 存储的属性
  • 覆盖func / var
  • 可用的function/ var
  • 需要(指定)初始化程序

这些限制(至less前三个)来自需要事先知道对象的数据布局(纯Swift的见证表)。 扩展可能会在运行时延迟加载(通过框架,插件,dlopen,…),创build实例后更改types的布局会使ABI瘫痪。

Swift团队的一个温和的build议:)

所有来自一个模块的代码都保证可以同时使用。 如果Swift编译器允许在单个模块中 “组合”types,则可以避免阻止完全分离function方面的限制。 对于组合types,我的意思是编译器将从模块中的所有文件中收集所有定义types布局的声明。 与其他方面的语言一样,它会自动查找内部文件依赖关系。

这将允许真正写出“面向方面”的扩展。 不需要在主声明中声明存储的属性或覆盖,就可以实现更好的访问控制和关注点分离。