我对Swift中面向协议编程的概念非常着迷,正因为如此,我将去年创建的一个旧项目(最初是一个OOP框架)移植到POP。
在这个阶段,我遇到的问题可能是因为我要么错误地理解POP,要么Swift 2.0 Betas没有创建一个真正的面向协议的框架(不太可能 - 如果有的话)可能会误解POP的某些方面。
面向协议的编程是不到一个月前向全世界介绍的一种全新的编程范式,因此没有太多关于它的书面内容(我在该主题上发现的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
的类,以及从其继承的另外两个类。所以他们免费获得了所有这些房产。
但看起来我不能将符合该协议的对象作为这些属性的默认实现(即getter)吗?
我尝试的第一件事,就是简单地将协议提供给我的struct声明,失败了,这是预期的,因为我没有为属性提供任何实现:
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)
}
这失败了:
类型'动漫'不符合协议'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中,您可以通过子类化来避免重复的代码。
所以我在这里重复一下我的问题:我是否误解了面向协议的编程,或者只要你使struct / class / enum符合它,你必须复制并粘贴特定于协议的逻辑,这是POP的缺点吗?
答案 0 :(得分:4)
我发布这个帖子已经有几个星期,但我相信Aaron Brager所说的是真的。
虽然面向协议的编程本身就是新的,但是协议的概念已经在Objective-C中存在了很长时间,它们在Swift中,并且它们在Java等语言中有它们的变体。由于协议和扩展的性质,看起来无法进行属性的默认实现,因为扩展不允许您在其中设置非计算属性。
答案 1 :(得分: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
但有一个好奇心:在协议中,为什么要将所有属性声明为只读?