在 Swift 中执行协议的默认实现时,实现默认属性的正确方法是什么?
What's the correct way to implement default properties when doing default implementation of protocols in Swift?
我对 Swift 中的面向协议编程的概念非常着迷,因此,我正在将去年创建的一个旧项目(最初是一个 OOP 框架)迁移到 POP .
在这个阶段,我遇到的问题可能是因为我对 POP 的理解不正确,或者 Swift 2.0 Betas 没有创建真正面向协议的框架的所有内容(不是很有可能 - 如果有的话,我可能会误解 POP 的某些方面)。
Protocol-Oriented Programming 是不到一个月前引入世界的全新编程范式,因此关于它的书面内容并不多(only tutorial 我在该主题上找到的没有解决我遇到的问题,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
的 class,另外两个 class 继承自它。所以他们免费获得了所有这些财产。
但我似乎无法为遵守该协议的对象提供这些属性的默认实现(即 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)
}
这失败了:
Type 'Anime' does not conform to protocol '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]
}
这没有用:
Extensions may not contain stored properties.
它有一个我非常不喜欢的缺点:我已经在复制代码,将协议的属性复制到扩展中。
所以最后,我永远无法让我的属性 "spread" 符合协议的对象,所以我最终将属性添加到 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 中,您可以通过 subclassing.
避免重复代码
所以我在这里重复我的问题:我是不是误解了面向协议的编程,或者 POP 的缺点是您必须复制并粘贴您的协议特定逻辑每当您使 struct/class/enum 符合是吗?
那是因为您的 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
但有一点好奇:在协议中,为什么要将所有属性声明为只读?
我发布这篇文章已经几周了,但我相信 Aaron Brager 所说的是真的。
虽然面向协议的编程本身相当新,但协议的思想在 Objective-C 中已经存在很长时间了,它们在 Swift 中,并且它们在Java 等语言。由于协议和扩展的性质,似乎无法对属性进行默认实现,因为扩展不允许您在其中设置非计算属性。
我对 Swift 中的面向协议编程的概念非常着迷,因此,我正在将去年创建的一个旧项目(最初是一个 OOP 框架)迁移到 POP .
在这个阶段,我遇到的问题可能是因为我对 POP 的理解不正确,或者 Swift 2.0 Betas 没有创建真正面向协议的框架的所有内容(不是很有可能 - 如果有的话,我可能会误解 POP 的某些方面)。
Protocol-Oriented Programming 是不到一个月前引入世界的全新编程范式,因此关于它的书面内容并不多(only tutorial 我在该主题上找到的没有解决我遇到的问题,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
的 class,另外两个 class 继承自它。所以他们免费获得了所有这些财产。
但我似乎无法为遵守该协议的对象提供这些属性的默认实现(即 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)
}
这失败了:
Type 'Anime' does not conform to protocol '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]
}
这没有用:
Extensions may not contain stored properties.
它有一个我非常不喜欢的缺点:我已经在复制代码,将协议的属性复制到扩展中。
所以最后,我永远无法让我的属性 "spread" 符合协议的对象,所以我最终将属性添加到 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 中,您可以通过 subclassing.
所以我在这里重复我的问题:我是不是误解了面向协议的编程,或者 POP 的缺点是您必须复制并粘贴您的协议特定逻辑每当您使 struct/class/enum 符合是吗?
那是因为您的 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
但有一点好奇:在协议中,为什么要将所有属性声明为只读?
我发布这篇文章已经几周了,但我相信 Aaron Brager 所说的是真的。
虽然面向协议的编程本身相当新,但协议的思想在 Objective-C 中已经存在很长时间了,它们在 Swift 中,并且它们在Java 等语言。由于协议和扩展的性质,似乎无法对属性进行默认实现,因为扩展不允许您在其中设置非计算属性。