2015-09-25 52 views
0

對於星期五樂趣,我想以可互換的格式爲角度建模。我不確定我是否以最快捷的慣用方式完成了它,但我正在學習。所以我有一個Angle協議,然後是3種不同的結構類型(Radians,Degrees和Rotations),它們都符合Angle協議。我想能夠加/減他們,但訣竅是,我想要lhs參數來指定返回類型。例如:Swift二元運算符用於返回接收器類型(符合協議)的協議

Degrees(180) + Rotations(0.25) --> Degrees(270) 

Rotations(0.25) + Radians(M_PI) -> Rotations(0.75) 

我希望是我可以做類似

func + (lhs:Angle, rhs:Angle) -> Angle { 
    return lhs.dynamicType(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

Angle協議需要一個var rawRadians:CGFloat { get }以及一個init(rawRadians:CGFloat)

我可以用Smalltalk-es做到這一點que雙重調度方法,但我認爲最適合使用更多Swift的方法(特別是需要更少代碼的代碼,雙重調度需要大量樣板代碼)。

回答

2

你只需要一個普通的加法:

func +<A: Angle>(lhs: A, rhs: Angle) -> A { 
    return A(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

通過這種方式,除了將返回任何類型的LHS。

一般來說,如果你使用dynamicType,你可能與Swift戰鬥。 Swift更依賴泛型和協議(即編譯時的靜態類型信息),而不是動態分派(即運行時的動態類型信息)。

在此代碼中,正如您所說,A是「某種類型的符合Angle的佔位符,需要在編譯時確定」。因此,在您第一個例子:

Degrees(180) + Rotations(0.25) --> Degrees(270) 

這實際上是調用一個專門的功能+<Degrees>。這個:

Rotations(0.25) + Radians(M_PI) -> Rotations(0.75) 

調用一個(邏輯上)不同的函數,稱爲+<Rotations>。編譯器可以選擇將這些函數優化爲單個函數,但邏輯上它們是在編譯時創建的獨立函數。這基本上是手寫addDegrees(Degrees, Angle)addRotations(Rotations, Angle)的捷徑。

現在,關於一個函數需要兩個角度並返回....現在,什麼?如果你想在這種情況下返回Angle,這很容易,正好和你原來的簽名一致:「返回Radians

func +(lhs: Angle, rhs: Angle) -> Angle { 
    return Radians(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

「但是......」你說,不,它不。它返回Angle。你可以做任何「角度」的事情就可以了。實現細節應該是不透明的。如果你關心底層的數據結構是Radians,那麼你幾乎肯定會做出錯誤的事情。

好的,有一個方面可能有助於瞭解這一點,也就是說,如果您根據自己的方式打印出來的內容。所以如果用戶給你開始度數信息,那麼你想打印所有度數(使用你沒有提到的description方法)。也許這是值得做的,你希望將特定case.If,你原來的代碼是非常接近:

func +(lhs: Angle, rhs: Angle) -> Angle { 
    return lhs.dynamicType.init(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

但它的關鍵是要理解,這不符合您的要求,有」 LHS參數決定返回類型「。這導致lhs參數指定返回實現。返回類型始終爲Angle。如果您想更改類型的返回,則需要使用泛型。