我对 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 的缺点是你必须在使结构/类/枚举符合它时复制和粘贴特定于协议的逻辑?