Я действительно очарован концепцией протокольно-ориентированного программирования в Swift, и поэтому я переношу старый проект, который я создал в прошлом году (который изначально был ООП-фреймворком), на POP.
На данном этапе проблемы, с которыми я сталкиваюсь, могут быть связаны с тем, что я либо неправильно понимаю POP, либо в бета-версиях Swift 2.0 нет всего, что нужно для создания действительно ориентированной на протокол структуры (маловероятно — если что-то я могу неправильно понять некоторые аспекты 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
и два других класса, унаследованных от него. Таким образом, они получили все эти свойства бесплатно.
Но не похоже, что я могу дать своим объектам, которые соответствуют этому протоколу, реализацию по умолчанию (а именно, геттеры) для этих свойств?
Первое, что я попробовал, а именно просто указать протокол для объявления моей структуры, потерпело неудачу, чего и следовало ожидать, поскольку я не предоставлял никакой реализации для свойств:
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
. В ООП вы избегаете дублирования кода путем создания подклассов.
Итак, я повторяю свой вопрос здесь: я неправильно понимаю протокольно-ориентированное программирование, или это недостаток POP, который заключается в том, что вам приходится копировать и вставлять свою специфичную для протокола логику всякий раз, когда вы приводите структуру/класс/перечисление в соответствие с ней?
MediaType
на отдельные более мелкие протоколы, а затем добавить методы к расширениям этих протоколов. Сила POP заключается в сочетании модульности, использования типов значений и реализации методов по умолчанию. - person Aaron Brager   schedule 05.07.2015