Swift代码中的嵌套命名法

2021-03-01 12:42:52 浏览数 (1)

Swift代码中的嵌套命名法

Swift支持与其他类型嵌套命名,尽管它还没有专用的命名关键词。下面我们来看看,如何使用类型嵌套来优化我们代码的结构。

大多数Swift开发者习惯于用类型在结构上的实际名字累命名。例如:PostTextFormatterOption(一个Text Formatter类型的用于format Posts的Option)。这可能是因为我们在Objective-C & C中,养成的别无选择的可怕命名习惯,被我们带到了Swift里。

下面我们用上面提到的类型作为例子,来看下Post、PostTextFormatter以及PostTextFormatterOption的实现:

代码语言:javascript复制
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的嵌套类型,会发生什么?

代码语言:javascript复制
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的文本的格式化看起来如下:

代码语言:javascript复制
let formatter = Post.TextFormatter(options: [.highlightLinks])
let text = formatter.formatText(for: post)

然而,使用嵌套类型还有一个不容忽视的坏处。代码看起来“反了”,因为父类型的实际内容被挤到了最下面。我们试着来修复一下这个问题,把嵌套类型的代码从上面移到下面(为了好分辨,还添加一些MARKs)

代码语言:javascript复制
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。这种方法可以在实现和调用时保持层级关系,同时清楚明白的分开每种类型。

看起来如下:

代码语言:javascript复制
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来实现类似嵌套类型的代码(实际上并没用嵌套类型)。尽管这种方法在实现上并没有嵌套层级关系,但是却减少了冗长代码————并且调用看起来也和使用嵌套类型一样。

代码如下:

代码语言:javascript复制
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。

感谢阅读!?

0 人点赞