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

Dan*_*idi 16 system-design ios swift swift-extensions

在Swift中,我历史上使用扩展来扩展封闭类型并提供方便的,无逻辑的功能,如动画,数学扩展等.但是,由于扩展是硬依赖性遍布整个代码库,我总是在实现之前三次思考某事作为延伸.

最近,我已经看到Apple建议在更大程度上使用扩展,例如将协议实现为单独的扩展.

也就是说,如果你有一个实现协议B的A类,你最终会得到这样的设计:

class A {
    // Initializers, stored properties etc.
}

extension A: B {
    // Protocol implementation
}
Run Code Online (Sandbox Code Playgroud)

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

fileprivate extension A {
    // Private, calculated properties
}

fileprivate extension A {
    // Private functions
}
Run Code Online (Sandbox Code Playgroud)

我的一部分就像你在单独的扩展中实现协议时获得的构建块.它使得该类的独立部分非常独特.但是,只要继承此类,就必须更改此设计,因为无法覆盖扩展函数.

我认为第二种方法是......有趣.一旦它成功,那就是你不需要注释每个私有属性并将其作为私有属性,因为你可以为扩展指定.

但是,这种设计还会分割存储和非存储的属性,公共和私有函数,使类的"逻辑"更难遵循(我知道写较小的类).这与子类化问题一起使我在延伸仙境的门廊上停了下来.

很想听听Swift社区如何看待扩展.你怎么看?有一个银色的小礼物吗?

Luc*_*rti 10

当然,这只是我的意见,所以我要写的很容易.

我目前正在使用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
}
Run Code Online (Sandbox Code Playgroud)

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

扩展说明

  • 它提醒我,Swift是一种面向协议的编程语言,而不是OOP语言.协议和协议扩展没有任何功能.我更喜欢使用协议为我的classes/struct添加安全层.例如,我通常以这种方式编写模型:

    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
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

通过这种方式,您仍然可以在类中编辑您的值uidnameUserModel,但是您不能在外部编辑,因为您只能处理User协议类型.


Nik*_*uhe 5

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

将类型的职责分为扩展

这些是我在各个扩展中加入的方面的示例:

  • 从客户端看到的类型的主界面.
  • 协议一致性(即代理协议,通常是私有的).
  • 序列化(例如所有NSCoding相关的).
  • 生活在后台线程上的类型的部分,如网络回调.

有时,当单个方面的复杂性上升时,我甚至会将类型的实现分割为多个文件.

以下是一些描述我如何对实现相关代码进行排序的细节:

  • 重点是功能成员资格.
  • 保持公共和私有实现关闭,但是分开.
  • 不要var和之间分开func.
  • 将功能实现的所有方面保持在一起:嵌套类型,初始化器,协议一致性等.

优点

分离类型方面的主要原因是使其更易于阅读和理解.

在阅读外国(或我自己的旧)代码时,了解大局通常是潜水中最困难的部分.让开发人员了解某种方法的上下文有很大帮助.

还有另一个好处:访问控制可以更容易地无意中调用某些内容.只应该从后台线程调用的方法可以private在"background"扩展中声明.现在它根本无法从其他地方调用.

目前的限制

Swift 3对这种风格施加了某些限制.有几件事只能存在于主要类型的实现中:

  • 存储的属性
  • 覆盖func/var
  • overidable func/var
  • 必需(指定)初始化者

这些限制(至少前三个)来自于必须事先知道对象的数据布局(以及纯Swift的见证表).扩展可能在运行时期间(通过框架,插件,dlopen,...)延迟加载,并且在创建实例后更改类型的布局会制止其ABI.

对Swift团队的一个温和的建议:)

保证一个模块的所有代码可以同时使用.如果Swift编译器允许在单个模块中 "组合"类型,则可以避免阻止完全分离功能方面的限制.对于组合类型,我的意思是编译器将从模块中的所有文件中收集定义类型布局的所有声明.与该语言的其他方面一样,它会自动找到文件内依赖项.

这将允许真正编写"面向方面"的扩展.不必在主声明中声明存储的属性或覆盖将实现更好的访问控制和关注点分离.