作者:Russ Bishop,原文鏈接,原文日期:2016-11-07
譯者:星夜暮晨;校對:walkingway;定稿:CMB
最近幾天,我在 Swift 用戶列表中參與了一個討論,主題是怎樣才能更好滴將包含字符串值的 JSON 數組轉換為枚舉集 (Enumeration Set)。我半開玩笑地建議:這些字符串值應該被轉換到基于字符串的枚舉當中,然后這些值的 hashValues
將用于設置標志位(flags)。
當然,我很快(并且理所應當)被質疑道:『最終的解決方案是否應該取決于 hashValue
的實現細節』—— 很顯然不應該。但是隨著我思考的深入,我猜想是否可以通過哈希值來引導選項集 (Option Sets) 的創建,從而消除潛在的錯誤呢?
我的意思是假設有這樣一個枚舉:
private enum LaundryFlags: String {
case lowWater, lowHeat, gentleCycle, tumbleDry
}
我們可以使用枚舉值來生成選項集,因為我們知道每個 hashValue
都不會發生重疊,這使得編譯器可以自行選擇實現的細節,而不用人工干涉:
public static let lowWater = LaundryOptions(rawValue:
1 << LaundryFlags.lowWater.hashValue)
這個方法讓我們可以從字符串中構建選項集,而無需關注具體的原始值 (Raw Value)。無論編譯器如何計算,最終的結果都是一致的:
// 基于字符串的初始化操作
public init(strings: [String]) {
let set: LaundryOptions = strings
.flatMap({ LaundryFlags(rawValue: $0) }) // 轉換為枚舉
.map({ 1 << $0.hashValue }) // 轉換為 Int,即標志值
.flatMap({ LaundryOptions(rawValue: $0) }) // 轉換為選項集
.reduce([]) { $0.union($1) } // 聯結
_rawValue = set.rawValue
}
不過這種做法也有限制。我們無法使用枚舉來表示如下所示的復合便利成員 (Compound Convenience Members):
public static let energyStar: LaundryOptions = [.lowWater, .lowHeat]
public static let gentleStar: LaundryOptions = energyStar.union(gentleCycle)
此外,我們可以很容易地實現 CustomStringConvertible
協議,即便原始值枚舉無法顯示它們的成員,并且也無法使用哈希值來進行初始化。如下面這段代碼所示,生成一個延遲成員 (Lazy Member) 字典并不困難。我們可以將這個實現變為樣板代碼 (Boiler Plate),然后將您的案例清單 (Case List) 復制/粘貼到這個數組當中:
static var memberDict: Dictionary<Int, String> =
[lowWater, lowHeat, gentleCycle, tumbleDry]
.reduce([:]) {
var dict = $0
dict[$1.hashValue] = "\($1)"
return dict
}
通過位運算,我們便可以從選項集當中規約 (Reduce) 出這個字典:
public var description: String {
let members = LaundryFlags.memberDict.reduce([]) {
return (rawValue & 1 << $1.key) != 0
? $0 + [$1.value] : $0
}
return members.joined(separator: ", ")
}
所以讓我們亮劍吧:考慮到這個方法是多么地簡單可靠,那我還能繼續遵循『原始值枚舉以及它們的哈希值從不使用實現細節』的約定么?
完整的 gist 代碼可在此查看。
本文由 SwiftGG 翻譯組翻譯,已經獲得作者翻譯授權,最新文章請訪問 http://swift.gg。