2015-07-04 2 views
1

Я действительно очарован концепцией протокола-ориентированного программирования в Swift, и из-за этого я перемещаю старый проект, который я создал в прошлом году (который первоначально был основой OOP) для POP.Каков правильный способ реализации свойств по умолчанию при выполнении по умолчанию протоколов в Swift?

На этом этапе проблемы, с которыми я столкнулся, могут быть связаны с тем, что я неправильно понимаю 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, и два другие классы, унаследованные от него. Таким образом, они получили все эти свойства бесплатно.

Но это не похоже, что я могу передать свои объекты, соответствующие этому протоколу, по умолчанию (а именно, геттеры) для этих свойств?

Первое, что я пытался, что просто дать протокол к моей структуре декларации, не удался, что и следовало ожидать, так как я не оказываю никакой реализации свойств:

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, который вы должны копировать и вставлять логику, специфичную для протокола, всякий раз, когда вы выполняете структуру/класс/перечисление ?

+1

Вы можете использовать * вычисленные * свойства в расширении протокола, но расширения не могут иметь регулярные свойства. Это ограничение останется. –

+0

Это определенно выглядит так, что в соответствии с этим ограничением не будет реализована реализация свойств по умолчанию в протоколах. Интересно подумать об этом. –

+1

Согласен. Я надеюсь, что расширения позволят хранить в будущем. –

ответ

3

Прошло несколько недель с тех пор, как я опубликовал это, но я верю, что Аарон Брагер сказал правду.

Хотя протокол-ориентированное программирование является скорее новым, идея протоколов присутствует в Objective-C в течение длительного времени, они находятся в Swift и имеют свои варианты на языках, таких как Java. Из-за характера протоколов и расширений, похоже, что реализация свойств по умолчанию невозможна, поскольку расширения не позволят вам устанавливать в них невычислимые свойства.

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 

Одно любопытство, хотя: в протоколе, почему вы объявляя все свойства, как только для чтения?

+0

Да, это то, что я говорю. Так нет ли способа выполнить реализацию свойств по умолчанию в протоколах? - Свойства доступны только для чтения, потому что все содержимое возвращается с сервера. Это не то, что пользователь может редактировать. –

+1

Ответ заключается в том, что это сложно. Тип, соответствующий протоколу, должен реализовывать все свойства, объявляемые протоколом. Тем не менее, для * методов * есть некоторые трюки, которые вы можете использовать: http://nshipster.com/swift-default-protocol-implementations/ –