В Swift вы можете определить перечисление и присвоить ему свойство через связанное значение, например:
protocol SizeEnum {
var length : Double? { get } // Length should be >= 0 - has to be an Optional for errors
}
enum SizesEnum : SizeEnum {
case Short(length : Double) // 0 <= length <= maxShort
case Long(length : Double) // length > maxShort
private static let maxShort = 1.0
var length : Double? {
get {
switch self {
case let .Short(length):
if length >= 0 && length <= SizesEnum.maxShort { // Need to error check every access
return length
}
case let .Long(length):
if length > SizesEnum.maxShort { // Need to error check every access
return length
}
}
return nil // There was an error
}
}
}
SizesEnum.Short(length: 0.5).length // [Some 0.5]
SizesEnum.Short(length: 2).length // nil
SizesEnum.Long(length: 2).length // [Some 2.0]
SizesEnum.Long(length: -1).length // nil
Однако это не идеально, потому что:
- Проверка ошибки для параметра длины может быть выполнена только при доступе, вы не можете перехватить инициализацию
- Параметр длины на удивление длинный.
Альтернативой, которая мне кажется лучше, является использование статической фабрики, например:
protocol SizeStruct {
var length : Double { get } // Length should be >= 0 - is *not* an Optional
}
struct SizesStruct : SizeStruct {
static func Short(length : Double) -> SizeStruct? {
if length >= 0 && length <= maxShort { // Check at creation only
return SizesStruct(length)
}
return nil
}
static func Long(length : Double) -> SizeStruct? {
if length > maxShort { // Check at creation only
return SizesStruct(length)
}
return nil
}
let length : Double
private static let maxShort = 1.0
private init(_ length : Double) {
self.length = length
}
}
SizesStruct.Short(0.5)?.length // [Some 0.5]
SizesStruct.Short(2)?.length // nil
SizesStruct.Long(2)?.length // [Some 2.0]
SizesStruct.Long(-1)?.length // nil
Учитывая, что статическое фабричное решение более аккуратное, когда мне на самом деле использовать перечисление со значениями? Я что-то упускаю? Есть ли убойный вариант использования?
В ответ на драваг
Для Optional
других языков, например. Java и Scala, вы используете фабрики, версия Java описана здесь: http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html фабрикой является of
метод.
В Swift вы бы сделали что-то вроде:
class Opt { // Note only Some stores the value, not None
//class let None = Opt() - class variables not supported in beta 4!
class Some<T> : Opt {
let value : T
init(_ value : T) {
self.value = value
}
}
private init() {} // Stop any other ways of making an Opt
}
Opt.Some(1).value // 1
Вероятно, это оптимальный пример для enum
, так как проверка ошибок не требуется, но даже при этом заводская версия конкурентоспособна. Пример Optional
настолько прост, что вам даже не нужна фабрика, вы просто создаете Some
напрямую. Обратите внимание, что None
не использует хранилище.
Пример со штрих-кодом показывает, насколько лучше фабричная техника; на практике не все коллекции из 4 Int
являются действительными UPCA, и не все String
являются действительным QR-кодом, поэтому вам нужна проверка ошибок, которая болезненна с enum
s. Вот заводская версия:
class Barcode { // Note seperate storage for each case
class UPCABarcode : Barcode {
let type : Int, l : Int, r : Int, check : Int
private init(type : Int, l : Int, r : Int, check : Int) {
(self.type, self.l, self.r, self.check) = (type, l, r, check)
}
}
class func UPCA(#type : Int, l : Int, r : Int, check : Int) -> UPCABarcode? {
if ok(type: type, l: l, r: r, check: check) {
return UPCABarcode(type: type, l: l, r: r, check: check)
}
return nil
}
class func QRCode(#s : String) -> Barcode? { // Have not expanded this case; use same pattern as UPCA
return Barcode()
}
private init() {} // Prevent any other types of Barcode
class func ok(#type : Int, l : Int, r : Int, check : Int) -> Bool {
return true // In practice has to check supported type, range of L and R, and if check digit is correct
}
}
Barcode.UPCA(type: 0, l: 1, r: 2, check: 3)
Если вы используете enum
версию Barcode
, то каждый раз, когда вы используете Barcode
, вы должны проверять его действительность, потому что нет ничего, что могло бы остановить неверные штрих-коды. В то время как заводская версия выполняет проверку при создании. Обратите внимание, что у Barcode
нет хранилища, а у UPCA
есть собственное хранилище. Я не кодировал QRCode
, потому что он использует тот же шаблон проектирования, что и UPCA
.
У меня сложилось впечатление, что версия enum
отлично выглядит в учебниках, но вскоре становится болезненной на практике из-за обработки ошибок.