2016-04-26 127 views
2

爲了簡單起見,結構鴨打字,我要簡化最終目標與斯威夫特

我試圖寫斯威夫特100%的通用PostProvider協議。 但是,由於「發佈」可能包含不同平臺上的不同信息,因此該協議必須是「一般類型」。以一個結構作爲其參數,它定義了任何基礎職位的結構。

struct Post { 
    // Only fields required by ALL posts 
    var id: UInt64 
    var title: String 
    var content: String 
} 

struct WPPost : Post { 
    // WordPress posts also have an author 
    var author: UInt64 
} 

struct DrupalPost : Post { 
    // Drupal posts also have an author 
    var author: UInt64 
    // and a 'last updated' timestamp 
    var lastUpdated: UInt64 
} 

protocol PostProvider<TPost : Post> { 
    func getPosts() -> TPost[] 
    // [...] 
} 

class WPPostProvider : PostProvider<WPPost> { 
    func getPosts() -> WPPost { 
     // [...] 
    } 
} 

class DrupalPostProvider : PostProvider<DrupalPost> { 
    func getPosts() -> DrupalPost { 
     // [...] 
    } 
} 

這些PostProviders將被其他代碼段使用。

爲了讓使用任何PostProvider的代碼儘可能重用,我希望任何需要PostProvider的方法都能夠輸入提示對象中必須包含哪些字段。

這樣,有點..

func myFunction(provider: PostProvider</*any struct that extends the 'Post' 
             struct and contains an 'author' 
             field of type 'UInt64'*/>) 

我還要做同樣的事情只是一個Post對象

func myFunction(post: /*any struct that extends the 'Post' struct and 
          contains an 'author' field of 
          type 'UInt64'*/) 

有人向我指出的@Alexander有在swift中沒有'結構繼承'。讓我補充一點,這不是重點。無論如何,這幾乎都是僞代碼。重點是我需要一些'typehinting'方法,只允許我'提示'所需的字段,對於某些數據結構

+0

如果您需要「擴展」您的文章並保留其類型信息,您應該使用類而不是結構。在Swift中沒有「struct擴展struct」這樣的事情。 –

+0

我在問題的底部添加了一條註釋。不過謝謝,我一直認爲swift對結構有繼承性。 – sigsve

回答

3

鴨子鍵入只是意味着「我需要一個具有以下方法的東西。」這是一個協議。在Swift中,沒有任何擴展'Post'結構的結構,並且包含'UInt64'類型的'作者'字段,因爲沒有「擴展'Post'結構。但是,絕對是「任何事情有型的gettable‘作者’字段‘UINT64’」:

protocol AuthorProviding { 
    var author: UInt64 { get } 
} 

func thingWithAuthor<T: AuthorProviding>(provider: T) { 
} 

如果你想「也有後場」,它只是另一種協議:

protocol Post { 
    // Only fields required by ALL posts 
    var id: UInt64 { get } 
    var title: String { get } 
    var content: String { get } 
} 

當然,你可以使一個AuthorProviding一個類型後,如果他們走在一起:

protocol AuthorProviding: Post { 
    var author: UInt64 { get } 
} 

但是,您可能還需要保留這些獨立:

protocol AuthorProviding { 
    var author: UInt64 { get } 
} 

func thingWithAuthor<T: protocol<Post, AuthorProviding>>(provider: T) { 
} 

這裏的關鍵是你不需要類和繼承。你只需通過協議說出你想要的方法。這不是「打字」。 Swift有很強的類型。沒有必要(或能力)暗示這些類型;你只是說他們是什麼。所有這一切都說,我的經驗是,試圖從一開始就使代碼過於通用只會使代碼變得非常複雜(令人驚訝的是)更脆弱,而且更難以改變。除非你正在編寫一個從一開始就知道的框架將被許多不同的組織(如Swift stdlib)使用的框架,否則我發現最好將泛型限制在你知道你會特別需要的部分。使用協議和泛型很容易陷入困境(特別是一旦涉及到相關類型),並且您發現自己花費大量時間和代碼來支付您永遠不會使用的靈活性。 Stack Overflow充滿了與「儘可能通用」代碼相關的難題。有一天Swift可能會是一個相當強大的通用語言,但今天它有許多尖銳的邊緣和令人驚訝的角落案例。

+0

這不是說傳遞給'thingWithAuthor'的任何東西都需要明確地採用'AuthorProviding'協議嗎?或者它是隱含的,只需讓'作者'字段符合'AuthorProving'類型? – sigsve

+0

它必須是明確的,但可以用空白擴展名將其明確。 '擴展SomeType:AuthorProviding {}'。這甚至可以在另一個包中完成,所以'SomeType'的原作者不需要知道你的協議。 –