感謝DJagger提供翻譯
在領域模型驅動設計(Domain Driven Design)[1]中體現了實體和值的區別,每個實體都有惟一的身份(identity),而值由它的屬性所決定。
NSFileHandle
是一個實體的例子:它封裝了一個檔案描述符。如果我們想知道兩個NSFileHandle
型別的物件是否相同,我們可以檢查他們是否引用同一個物件。檔案的控制程式碼有生命週期,會隨著時間改變。如果我們有兩個不同的檔案控制程式碼(各自的檔案描述符不相同),即便其他屬性相同,我們也要對它們區別對待,應該分別關閉這兩個檔案描述符。
比如NSURL
就是一個值——它是一種不可變的物件,通過屬性確認身份。如果兩個NSURL
物件表示的URL相同,那麼這兩個物件就是相同的。URL是固定的,不會隨著時間改變。
當我們要在實體和值之間做出選擇時,一個最重要的問題就是:*“它需要確定的身份嗎?”*在檔案控制程式碼的例子中,回答是肯定的,我們需要分開管理每個檔案控制程式碼的生命週期。而在URL的例子中,我們關心它們表示的是否是同一個URL而非同一個引用。
在幾乎所有的程式中,有許多的實體扮演者重要的角色。檔案描述符、通知中心、網路介面、資料庫記錄、輸入裝置、應用以及快取都是實體,因為它們都有自己的生命週期和惟一的身份。
值在軟體中也有很多的用處。URL、二進位制資料、日期、錯誤、字串、通知以及數字只定義了它們的屬性,它們不隨著時間改變。使用了不可變值的程式碼更好理解,不可變性直接確保了他們是執行緒安全的:任何不可變的的東西線上程之間共享時都是安全的。
當我們需要區分值和引用時,應用的領域是決定因素之一。舉個例子,在一個展示聯絡人資訊的應用中,地址應該被定義為值型別,它的身份就是它的值。如果有很多聯絡人共用一個地址,我們必然不希望改動一個人的地址資訊時也改動其他人的地址。然而在拼車的應用中,我們應該會用實體來表示地址:如果多人共用一個地址,我們希望它們拼一輛車。
在Swift中定義新型別時,我們可以在結構體、列舉和類中選擇。類在大多數情況下最適合實體:類在預設情況下時可變的,而且類的例項會隨著時間改變它們的狀態。
使用值型別時,結構體通常是最適合的。我們可以通過編譯器強制結構體不可變。換句話說,編譯器可以保證我們的型別有值語義。在某些情況下,類也可能是值型別的最佳選擇——比如我們需要與Objective-c連線。不過這時候我們需要自己保證不可變性。
當你需要在結構體和類之間做出選擇時,我們建議你考慮等價性(equality)。是否根據引用比較例項是否相同,換句話說,兩個有一樣資料的例項會被認為是不同的嗎?如果答案是肯定的話,類更加適合。 我們還可以思考一下,是否能通過包含的資料判斷例項是否相同?是的話,則結構體更適合。
當與Cocoa和Objective-C對接時,我們幾乎總是需要使用類。舉個例子,當實現一個table view的代理時,我們只能用類,別無選擇。許多的蘋果框架也嚴重依賴於子類。然而,面對不同領域的問題,我們可能仍需要設計具有值語義的類。舉個例子來說,在Core Image框架中,CIImage
物件是不可改變的:他們表示了一個再不能改變的影象。
在本章的剩餘部分,我們會說明結構體和類在行為上的不同,這讓你更容易決定把你的型別設計為類還是結構體。
##譯者注 [1]:領域模型驅動設計參考Richie的部落格