Swift 代码中的嵌套命名法#
Swift 支持与其他类型嵌套命名,尽管它还没有专用的命名关键词。下面我们来看看,如何使用类型嵌套来优化我们代码的结构。
大多数 Swift 开发者习惯于用类型在结构上的实际名字累命名。例如:PostTextFormatterOption (一个 Text Formatter 类型的用于 format Posts 的 Option)。这可能是因为我们在 Objective-C & C 中,养成的别无选择的可怕命名习惯,被我们带到了 Swift 里。
下面我们用上面提到的类型作为例子,来看下 Post、PostTextFormatter 以及 PostTextFormatterOption 的实现:
struct Post {
let id: Int
let author: User
let title: String
let text: String
}
class PostTextFormatter {
private let options: Set
init(options: Set) {
self.options = options
}
func formatTitle(for post: Post) -> String {
return post.title.formatted(withOptions: options)
}
func formatText(for post: Post) -> String {
return post.text.formatted(withOptions: options)
}
}
enum PostTextFormatterOption {
case highlightNames
case highlightLinks
}
然后我们再来看一下,如果我们把上面的类型作为 Post 的嵌套类型,会发生什么?
struct Post {
class TextFormatter {
enum Option {
case highlightNames
case highlightLinks
}
private let options: Set
init(options: Set) {
self.options = options
}
func formatTitle(for post: Post) -> String {
return post.title.formatted(withOptions: options)
}
func formatText(for post: Post) -> String {
return post.text.formatted(withOptions: options)
}
}
let id: Int
let author: User
let title: String
let text: String
}
上面嵌套类型的一个很大的优点是,我们扫一眼就可以很明白的看到类型之间的的结构和关系。而且我们还减少了初始化代码的冗长,使它更短更易阅读 (options 参数类型从 Set< PostTextFormatterOption> 变为 Set< Option >)。
调用层级也更加简洁明了 —— 所有和 Post 有关的整洁的变为 Post.namespace。一个 post 的文本的格式化看起来如下:
let formatter = Post.TextFormatter(options: [.highlightLinks])
let text = formatter.formatText(for: post)
然而,使用嵌套类型还有一个不容忽视的坏处。代码看起来 “反了”,因为父类型的实际内容被挤到了最下面。我们试着来修复一下这个问题,把嵌套类型的代码从上面移到下面(为了好分辨,还添加一些 MARKs)
struct Post {
let id: Int
let author: User
let title: String
let text: String
// MARK: - TextFormatter
class TextFormatter {
private let options: Set
init(options: Set) {
self.options = options
}
func formatTitle(for post: Post) -> String {
return post.title.formatted(withOptions: options)
}
func formatText(for post: Post) -> String {
return post.text.formatted(withOptions: options)
}
// MARK: - Option
enum Option {
case highlightNames
case highlightLinks
}
}
}
嵌套类型放上面还是下面纯粹是个人偏好。我比较喜欢把父类型的内容放在上面 ———— 同时还可以享受嵌套类型的便利。
事实上,在 Swift 中还有好几种其他方法可以实现命名、嵌套类型。
使用 extension 实现嵌套类型#
另一个实现嵌套类型的选择就是 extension。这种方法可以在实现和调用时保持层级关系,同时清楚明白的分开每种类型。
看起来如下:
struct Post {
let id: Int
let author: User
let title: String
let text: String
}
extension Post {
class TextFormatter {
private let options: Set
init(options: Set) {
self.options = options
}
func formatTitle(for post: Post) -> String {
return post.title.formatted(withOptions: options)
}
func formatText(for post: Post) -> String {
return post.text.formatted(withOptions: options)
}
}
}
extension Post.TextFormatter {
enum Option {
case highlightNames
case highlightLinks
}
}
使用 typealiases#
也可以使用 typealiases。在原始代码里添加 typealiases 来实现类似嵌套类型的代码(实际上并没用嵌套类型)。尽管这种方法在实现上并没有嵌套层级关系,但是却减少了冗长代码 ———— 并且调用看起来也和使用嵌套类型一样。
代码如下:
struct Post {
typealias TextFormatter = PostTextFormatter
let id: Int
let author: User
let title: String
let text: String
}
class PostTextFormatter {
typealias Option = PostTextFormatterOption
private let options: Set
init(options: Set) {
self.options = options
}
func formatTitle(for post: Post) -> String {
return post.title.formatted(withOptions: options)
}
func formatText(for post: Post) -> String {
return post.text.formatted(withOptions: options)
}
}
enum PostTextFormatterOption {
case highlightNames
case highlightLinks
}
最后#
使用嵌套类型有助于写出优雅结构、层级的代码,使多种类型之间的关系更加清楚明了 ———— 不管是在实现上,还是调用上。
然而,由于实现方法的不同,可能会遇到不同的挑战和副作用 ———— 所以我认为根据实际情况选择对应的实现很重要,为了赢得漂亮。
你认为呢?上面那些技术,你倾向于用哪种?或者你还有其他的方法?告诉我你的问题、看法,Twitter@johnsundell。
感谢阅读!🚀