2

我对 Swift 中面向协议编程的概念非常着迷,因此,我将去年创建的一个旧项目(最初是一个 OOP 框架)迁移到 POP。

在这个阶段,我遇到的问题可能是因为我对 POP 的理解不正确,或者 Swift 2.0 Beta 没有创建一个真正面向协议的框架的一切(不太可能 - 如果有的话我可能会误解POP 的某些方面)。

面向协议的编程是不到一个月前向世界介绍的一种全新的编程范式,因此关于它的书面内容并不多(我在该主题上找到的唯一教程并没有解决我遇到的问题,并且WWDC 视频也没有)。

无论如何,直截了当:我要么在这里做错了,要么面向协议编程的缺点之一是你必然会重复很多代码。一个例子:

我有以下协议,它具有许多属性并且也符合Equatable协议:

protocol MediaType : Equatable {

    /// MARK: - Properties

    /// The ID of the media, as assigned by MyAnimeList.
    var ID: Int { get }

    /// The title of the media.
    var title: String { get }

    /// Other titles by which this anime may be commonly known (only available in details requests).
    var otherTitles: (synonyms: [String], english: [String], japanese: [String])? { get }

    /// The global ranking of the title (only available in anime details requests).
    var rank: Int? { get }

    /// Rank of the title based on popularity (number of people adding title to the list) (only available in details requests).
    var popularityRank: Int? { get }

    /// URL to a representative image of the title. Usually a "cover" image.
    var imageURL: String { get }

    /// A list of adaptations of this media, or other media on which this media is based (only available in details requests).
    var adaptations: Relationships { get }

    /// The user's rating of the media.
    var memberScore: Float { get }

    /// Number of MyAnimeList members that that added the title to their list (only available in details requests).
    var membersCount: Int? { get }

    /// The number of MyAnimeList members that have this title on their favorites list (only available in details requests).
    var favoritedCount: Int? { get }

    /// A short HTML-formatted description of the media.
    var synopsis: String { get }

    /// A list of genres for this title (only available in details requests).
    var genres: [String]? { get }

    /// Popular tags for the title as assigned by MyAnimeList members (only available in details requests).
    var tags: [String] { get }
}

在我的框架的原始版本中,这个协议是一个名为 的类Media,以及从它继承的另外两个类。所以他们免费获得了所有这些房产。

但看起来我不能为符合该协议的对象提供这些属性的默认实现(即 getter)?

我尝试的第一件事是简单地将协议提供给我的结构声明,但失败了,这是意料之中的,因为我没有为属性提供任何实现:

struct Anime : MediaType {

    /// MARK: - MediaType

}

/// Compares two Anime_ objects. Two Anime_ objects are considered equal when they have the same ID and title.
func ==(lhs: Anime, rhs: Anime) -> Bool {
    return (lhs.ID == rhs.ID) && (lhs.title == rhs.title)
}

这失败了:

类型“Anime”不符合协议“MediaType”

我的下一个尝试是为 MediaType 编写一个扩展,并将属性放在那里:

extension MediaType {
    /// The ID of the media, as assigned by MyAnimeList.
    let ID: Int

    /// The title of the media.
    let title: String

    /// Other titles by which this anime may be commonly known (only available in details requests).
    let otherTitles: (synonyms: [String], english: [String], japanese: [String])?

    /// The global ranking of the title (only available in anime details requests).
    let rank: Int?

    /// Rank of the title based on popularity (number of people adding title to the list) (only available in details requests).
    let popularityRank: Int?

    /// URL to a representative image of the title. Usually a "cover" image.
    let imageURL: String

    /// A list of adaptations of this media, or other media on which this media is based (only available in details requests).
    let adaptations: Relationships

    /// The user's rating of the media.
    let memberScore: Float

    /// Number of MyAnimeList members that that added the title to their list (only available in details requests).
    let membersCount: Int?

    /// The number of MyAnimeList members that have this title on their favorites list (only available in details requests).
    let favoritedCount: Int?

    /// A short HTML-formatted description of the media.
    let synopsis: String

    /// A list of genres for this title (only available in details requests).
    let genres: [String]?

    /// Popular tags for the title as assigned by MyAnimeList members (only available in details requests).
    let tags: [String]
}

这没有用:

扩展可能不包含存储的属性。

它有一个我真的不喜欢的缺点:我已经在复制代码,将协议的属性复制到扩展中。

所以最后,我永远无法让我的属性“传播”到符合协议的对象,所以我最终将属性添加到Anime结构中。

struct Anime : MediaType {

    /// MARK: - MediaType

    /// The ID of the media, as assigned by MyAnimeList.
    let ID: Int

    /// The title of the media.
    let title: String

    /// Other titles by which this anime may be commonly known (only available in details requests).
    let otherTitles: (synonyms: [String], english: [String], japanese: [String])?

    /// The global ranking of the title (only available in anime details requests).
    let rank: Int?

    /// Rank of the title based on popularity (number of people adding title to the list) (only available in details requests).
    let popularityRank: Int?

    /// URL to a representative image of the title. Usually a "cover" image.
    let imageURL: String

    /// A list of adaptations of this media, or other media on which this media is based (only available in details requests).
    let adaptations: Relationships

    /// The user's rating of the media.
    let memberScore: Float

    /// Number of MyAnimeList members that that added the title to their list (only available in details requests).
    let membersCount: Int?

    /// The number of MyAnimeList members that have this title on their favorites list (only available in details requests).
    let favoritedCount: Int?

    /// A short HTML-formatted description of the media.
    let synopsis: String

    /// A list of genres for this title (only available in details requests).
    let genres: [String]?

    /// Popular tags for the title as assigned by MyAnimeList members (only available in details requests).
    let tags: [String]

    /// MARK: - Anime


}

这似乎奏效了。MediaType但现在我在和中都有我的属性Anime。在 OOP 中,您可以通过子类化来避免重复代码。

所以我在这里重复我的问题:我是不是误解了面向协议的编程,或者是 POP 的缺点是你必须在使结构/类/枚举符合它时复制和粘贴特定于协议的逻辑?

4

2 回答 2

4

自从我发布这篇文章以来已经有几个星期了,但我相信 Aaron Brager 所说的是真的。

虽然面向协议编程本身是相当新的,但协议的概念在 Objective-C 中已经存在很长时间了,它们在 Swift 中,并且它们在诸如 Java 之类的语言中有它们的变体。由于协议和扩展的性质,似乎不可能对属性进行默认实现,因为扩展不允许您在其中设置非计算属性。

于 2015-07-24T20:11:57.320 回答
1

那是因为您的Anime结构没有实现MediaType协议中的所有属性。这是一个最小化的示例,说明如何做到这一点:

protocol MediaType : Equatable {
    var ID: Int { get }
    var title: String { get }
}

struct Anime : MediaType {
    // Implement the MediaType protocol
    var ID : Int
    var title : String
}

/// Compares two Anime_ objects. Two Anime_ objects are considered equal when they have the same ID and title.
func ==(lhs: Anime, rhs: Anime) -> Bool {
    return (lhs.ID == rhs.ID) && (lhs.title == rhs.title)
}

let x = Anime(ID: 1, title: "title 1")
let y = Anime(ID: 2, title: "title 2")
let z = Anime(ID: 1, title: "title 1")

println(x == y) // false
println(x == z) // true

不过有一个好奇心:在协议中,为什么要将所有属性声明为只读?

于 2015-07-04T21:05:43.863 回答